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