Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ QRunnable-如何在没有比赛的情况下实现WaitForFinished?_C++_Multithreading_Qt - Fatal编程技术网

C++ QRunnable-如何在没有比赛的情况下实现WaitForFinished?

C++ QRunnable-如何在没有比赛的情况下实现WaitForFinished?,c++,multithreading,qt,C++,Multithreading,Qt,我在Qt中有一些I/O代码被移动到QRunnable,以避免阻塞UI线程。这将使用一些排队的插槽回调以指示其进度 然而,我最近看到了一些问题,当QRunnable的所有者被销毁时,它无法删除runnable,直到其完成(否则,由于工作线程仍在使用它而崩溃)。为了解决这个问题,我想在runnable中添加一个“WaitForFinished”,它将调用它的in-dtor,这样我就可以简单地删除QRunnable,并知道它将阻塞,直到它的dtor退出 我认为这应该很容易,因为我所需要做的就是在QRu

我在Qt中有一些I/O代码被移动到
QRunnable
,以避免阻塞UI线程。这将使用一些排队的插槽回调以指示其进度

然而,我最近看到了一些问题,当
QRunnable
的所有者被销毁时,它无法删除runnable,直到其完成(否则,由于工作线程仍在使用它而崩溃)。为了解决这个问题,我想在runnable中添加一个“
WaitForFinished
”,它将调用它的in-dtor,这样我就可以简单地删除
QRunnable
,并知道它将阻塞,直到它的dtor退出

我认为这应该很容易,因为我所需要做的就是在
QRunnable::run
实现中使用
QMutex
,然后在
WaitForFinished
中获取互斥体。然而,这有一个比赛条件,我不知道如何解决

考虑以下几点:

  • QRunnable
    已排队等待执行
  • QRunnable
    开始执行,但尚未调用
    QRunnable::run
  • 主线程删除
    QRunnable
    ,它在dtor中调用
    WaitForFinished
    ,并在
    QRunnable::run
    之前获取
    QMutex
    ——因此我们假设它没有运行或运行完成,并销毁
    QRunnable
    ,但它甚至还没有启动
    您应该在构建runnable时获取互斥锁,并在
    run()
    即将结束时释放它。这必须包括当
    run()
    根本不返回时-抛出的异常不是返回!在以下两种情况下,不能任意删除runnable:

  • 一旦提交到线程池,在完成之前

  • 如果设置了自动删除

    在线程池的实现中返回
    QRunnable::run()
    后,如果缓存的
    autoDelete
    值为true,则runnable中的内部引用计数器将递减。这将导致悬空指针取消引用

  • 下面是这个想法的一个经过测试的实现

    输出为:

    ~Incrementer 
    1 
    ~Incrementer 
    2 
    
    #包括
    #包括
    #包括
    #包括
    //这不能是可从派生的类,因为析构函数
    //将在派生类的析构函数之后运行-因此它不会
    //保护派生类的成员不被过早破坏。
    类RunnableRapper Q_DECL_FINAL:public QRunnable{
    Q_禁用_复制(RunnableRapper)
    类锁紧器{
    Q_禁用_复制(锁定锁)
    QMutexLocker*m_mutexLocker;
    公众:
    显式LockerUnlocker(QMutexLocker*m):m_mutexLocker(m){}
    ~LockerUnlocker(){m_mutexLocker->unlock();}
    };
    QRunnable*m_-wrapped;
    qmutexm_互斥体;
    QMutexLocker m_lock;
    无效运行()Q_DECL_FINAL{
    锁止器解锁器(&m_锁);
    m_wrapped->run();
    }
    公众:
    RunnableWrapper(QRunnable*r):m_包装(r)、m_锁(&m_互斥){
    setAutoDelete(false);
    }
    void wait(){QMutexLocker锁(&m_互斥锁);}
    ~runnablerrapper(){
    等待();
    如果(m_wrapped->autoDelete())删除m_wrapped;
    }
    };
    类递增器:公共QRunnable{
    int*m_val;
    void run()Q_DECL_覆盖{++*m_val;}
    公众:
    显式递增器(int*val):m_val(val){}
    ~Incrementer(){qDebug()首先,没有一刀切的解决方案。首选方法将取决于任务的详细信息。虽然我问了一些问题,但仍然有很多不明确之处。不过,我将介绍我能想到的最简单的方法

  • 启用可运行文件自动删除
  • 创建一些
    startedjobscenter
    ,并在每次在所有者对象中启动新作业时增加它
  • 创建一个信号量
    finishedjobsem
    当作业完成时发布该作业
  • 所有可运行项的
    waitForFinished
    如下所示

    完工后的粗骨料(标准作业中心)

  • 您可以在可运行析构函数中逐个释放
    finishedjobsem

  • startedjobscenter
    读取和写入之间没有竞争,因为它们都是在所有者的对象线程中执行的


    由于所有作业都将在所有者对象被销毁之前完成,因此没有任何方法
    finishedjobsem
    将无效地使用runnable表单。

    如果其他人对runnable有此问题,这里是我最终解决它的方法。我的用例基本上是我想要执行“run”方法在另一个线程中创建一个对象,并知道其何时完成/能够等待它

    我发现
    QtConcurrent
    实际上比
    QRunnable
    好得多

    class MyClass
    {
    public:
     void run() { }
    };
    
    void someFunc()
    {
      MyClass instance;
    
      // Async execute "run" on "instance", returns a future
      auto future = QtConcurrent::run( std::bind(&MyClass::run,instance) );
    
      // Will emit "finished" signal when done - should keep this and the future in scope of course!
      QFutureWatcher watcher;
      watcher.setFuture( future );
    
      // Blocks until MyClass::run() returns
      future.waitForFinished();
    }
    

    如果您设置autodelete,QRunnable可以被Qt系统删除。为什么您不想使用此功能?无论哪种方式,我都希望在创建它的对象被销毁后停止runnable。操作顺序是否重要:删除所有者对象并删除它启动的runnable对象?我的意思是,如果runnable将l将在所有者对象后不久终止,是允许的吗?否,因为runnable与在可派生runnable的析构函数中等待的所有者共享数据是否:当基类的析构函数运行时,派生类的析构函数已经擦除了它,而线程仍在使用它。因此,您可以显式调用
    wait()
    在每个派生类的析构函数中,或者——正如我所建议的那样——您使用一个最终包装类来管理其他可运行类的生命,并用作一个替代类。您不仅“可以”在析构函数中释放FinishedJobsem,还必须在那里执行。如果在
    运行()结束时执行此操作
    method,您有一个竞争。在析构函数中执行此操作是唯一安全的地方。此外,在析构函数中执行此操作的方式也有限制:如果有任何依赖于