Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QtConcurrent: Early stop a MappedReduced/FilteredReduced from inside
QtWS25 Last Chance

QtConcurrent: Early stop a MappedReduced/FilteredReduced from inside

Scheduled Pinned Locked Moved Solved General and Desktop
qtconcurrentmappedreducedcancel
4 Posts 2 Posters 478 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V Offline
    V Offline
    VTiTux
    wrote on last edited by VTiTux
    #1

    I am developping a "contains" like function, using the QtConcurrent MappedReduced function, to compute values from data stored in a list, then compare them to the value to search for.

    To save time, I would like to stop the process, when I found the first match.

    Is there a way to do that?

    I tried to capture the QFuture, to cancel the process from the Reduce functor (that is call by only one thread at a time), but that crashes:

    float compute(const QString &);
    bool compare(const float &, const float &);
    bool contains(const float &search, const QStringList &list)
    {
        QFuture<bool> process;
    
        std::function<float(const QString &)> map = [](const QString &data) { return compute(data); };
    
        std::function<void(bool&, const float &)> reduce = [&search, &process](bool &found, const float &value)
        {
            if( compare(search, value) == true )
            {
                if( found == false )
                    process.cancel();
                found = true;
                //throw found;
            }
        };
    
        process = QtConcurrent::mappedReduced<bool>(list, map, reduce, false);
        process.waitForFinished();
        return process.result();
    }
    

    Alternatively, I can share the result with the mapped functor, to skip the following computations, once a result has been found.

    1 Reply Last reply
    0
    • V Offline
      V Offline
      VTiTux
      wrote on last edited by
      #2

      I found!
      This was not a "multi threads" issue. The crash was from the return statement. This code works:

      float compute(const QString &);
      bool compare(const float &, const float &);
      bool contains(const float &search, const QStringList &list)
      {
          QFuture<bool> process;
          int count = 0;
          bool found = false;
      
          std::function<float(const QString &)> map = [](const QString &data) { return compute(data); };
      
          std::function<void(bool&, const float &)> reduce = [&found, &count, &search, &process](bool &, const float &value)
          {
              if( compare(search, value) == true )
              {
                  if( found == false )
                      process.cancel();
                  found = true;
              }
              ++count;
          };
      
          process = QtConcurrent::mappedReduced<bool>(list, map, reduce);
          process.waitForFinished();
          qDebug() << count;
          return found;
      }
      

      (probably safer with a QFutureWatcher)

      But I also read this old thread, and yeye_olive oriented me to this other solution:

      
      float compute(const QString &);
      bool compare(const float &, const float &);
      bool contains(const float &search, const QStringList &list)
      {
          int count = 0;
          bool found = false;
          QFutureWatcher<bool> watcher;
          QEventLoop loop;
          QObject::connect(&watcher, &QFutureWatcher<bool>::finished, &loop, &QEventLoop::quit);
      
          std::function<float(const QString &)> map = [](const QString &data) { return compute(data); };
      
          std::function<void(bool&, const float &)> reduce = [&found, &count, &search, &loop](bool &, const float &value)
          {
              if( compare(search, value) == true )
              {
                  if( found == false )
                      QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
                  found = true;
              }
              ++count;
          };
      
          watcher.setFuture( QtConcurrent::mappedReduced<bool>(list, map, reduce) );
          loop.exec();
          watcher.cancel();
          watcher.waitForFinished();
          qDebug() << count;
          return found;
      }
      

      And finally, I implemented this solution, which save the reduce functor (the half of the threads):

      
      class Result : public QObject
      {
      public:
          Result(QObject *parent=nullptr) : QObject(parent), mFound(false){}
      signals:
          void stop();
      public slots:
          void found(void)
          {
              mFound = true;
              emit stop();
          }
      public:
          bool mFound;
          Q_OBJECT
      };
      
      float compute(const QString &);
      bool compare(const float &, const float &);
      bool contains(const float &search, const QStringList &list)
      {
          int count = 0;
          Result result;
          QFutureWatcher<bool> watcher;
          QEventLoop loop;
          QObject::connect(&watcher, &QFutureWatcher<bool>::finished, &loop, &QEventLoop::quit);
          QObject::connect(&result, &Result::stop, &loop, &QEventLoop::quit);
      
          std::function<float(const QString &)> map = [&result, &count, &search](const QString &data)
          {
              ++count;
              if( compare(search, compute(data)) == true )
                  QMetaObject::invokeMethod(&result, "found", Qt::QueuedConnection);
              return false;    // need to return a value
          };
      
          watcher.setFuture( QtConcurrent::mapped(list, map) );
          loop.exec();
          watcher.cancel();
          watcher.waitForFinished();
          qDebug() << count;
          return result.mFound;
      }
      

      Are these solutions totally "thread-safe"? I don't really have experience in "multi-threading" coding.

      1 Reply Last reply
      0
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi,

        Out of curiosity, why implement it that way ? You are in fact blocking your application because you are waiting on the result.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        0
        • V Offline
          V Offline
          VTiTux
          wrote on last edited by
          #4

          No, it's not about a performance issue.
          I was just not satisfied with leaving this function running until the end, since I already had my result and the rest will not bring me anything more.
          If I need to write it in a simple 'foreach' loop, I would write something like

          for( value : list )
              if( value == search )
                  return true;
          return false;
          

          And I wanted to keep the same spirit with 'mappedReduced' (as long as this doesn't violate any "thread-rules" and without having to "hack" QtConcurrent).

          But yes, in fact I'm blocking my application untile I receive the results. My other methods that need to perform an action on all the items use 'blockingMapped', 'blockingMappedReduced' and 'blockingFiltered'. It's for a library that I am writing. And the applications that will use it are procedurale, not a GUI (or, maybe later...who knows?). But they may have to handle very long lists, and the 'compute' function are heavy. So QtConcurrent is a welcome help.

          I think I'll keep the first solution, the most elegant one in my eyes, if I don't get any warning.

          1 Reply Last reply
          0

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved