让任意线程执行一个匿名函数

本类主要功能是在当前线程(比如说主线程),指派任意一个线程(比如说某个工作线程)去执行一个匿名函数。

注意,这个和QtConcurrent配合QThreadPool不一样,QtConcurrent配合QThreadPool只能指派回调到QThreadPool中的线程。

而这个类可以指派一个回调到任意线程。

两个主要接口

JasonQt_InvokeFromThread::invoke:非阻塞,只是将回调放到队列中,等待执行

JasonQt_InvokeFromThread::waitForInvoke:阻塞,函数执行完成后才回返回

代码部分:

.h文件内容:

[cpp] view plain copy
 
  1. #include <QThread>  
  2. #include <QMap>  
  3. #include <QDebug>  
  4.   
  5. class JasonQt_InvokeFromThreadHelper: public QObject  
  6. {  
  7.     Q_OBJECT  
  8.   
  9. private:  
  10.     QMutex m_mutex;  
  11.     QList< std::function<void()> > m_waitCallbacks;  
  12.   
  13. public:  
  14.     void addCallback(const std::function<void()> &callback);  
  15.   
  16. public slots:  
  17.     void onRun();  
  18. };  
  19.   
  20. class JasonQt_InvokeFromThread  
  21. {  
  22. private:  
  23.     static QMutex g_mutex;  
  24.     static QMap< QThread *, JasonQt_InvokeFromThreadHelper * > g_helpers;  
  25.   
  26. public:  
  27.     static void invoke(QThread *thread, const std::function<void()> &callback);  
  28.   
  29.     static void waitForInvoke(QThread *thread, const std::function<void()> &callback);  
  30. };  


.cpp文件内容

[cpp] view plain copy
 
  1. // JasonQt_InvokeFromThreadHelper  
  2. void JasonQt_InvokeFromThreadHelper::addCallback(const std::function<void ()> &callback)  
  3. {  
  4.     m_mutex.lock();  
  5.     m_waitCallbacks.push_back(callback);  
  6.     m_mutex.unlock();  
  7. }  
  8.   
  9. void JasonQt_InvokeFromThreadHelper::onRun()  
  10. {  
  11.     if(!m_waitCallbacks.isEmpty())  
  12.     {  
  13.         m_mutex.lock();  
  14.   
  15.         auto callback = m_waitCallbacks.first();  
  16.         m_waitCallbacks.pop_front();  
  17.   
  18.         m_mutex.unlock();  
  19.   
  20.         callback();  
  21.     }  
  22. }  
  23.   
  24. // JasonQt_InvokeFromThread  
  25. QMutex JasonQt_InvokeFromThread::g_mutex;  
  26. QMap< QThread *, JasonQt_InvokeFromThreadHelper * > JasonQt_InvokeFromThread::g_helpers;  
  27.   
  28. void JasonQt_InvokeFromThread::invoke(QThread *thread, const std::function<void ()> &callback)  
  29. {  
  30.     if(!(thread->isRunning()))  
  31.     {  
  32.         qDebug() << "JasonQt_InvokeFromThread::invoke: Target thread" << thread << "is not running!";  
  33.         return;  
  34.     }  
  35.   
  36.     g_mutex.lock();  
  37.   
  38.     auto it = g_helpers.find(thread);  
  39.   
  40.     if(it == g_helpers.end())  
  41.     {  
  42.         auto helper = new JasonQt_InvokeFromThreadHelper;  
  43.         helper->moveToThread(thread);  
  44.   
  45.         QObject::connect(thread, &QThread::finished, [=]()  
  46.         {  
  47.             g_mutex.lock();  
  48.   
  49.             auto it = g_helpers.find(thread);  
  50.             if(it != g_helpers.end())  
  51.             {  
  52.                 g_helpers.erase(it);  
  53.             }  
  54.   
  55.             g_mutex.unlock();  
  56.         });  
  57.   
  58.         g_helpers.insert( thread, helper );  
  59.         it = g_helpers.find(thread);  
  60.     }  
  61.   
  62.     it.value()->addCallback(callback);  
  63.   
  64.     QMetaObject::invokeMethod(it.value(), "onRun", Qt::QueuedConnection);  
  65.   
  66.     g_mutex.unlock();  
  67. }  
  68.   
  69. void JasonQt_InvokeFromThread::waitForInvoke(QThread *thread, const std::function<void ()> &callback)  
  70. {  
  71.     if(!(thread->isRunning()))  
  72.     {  
  73.         qDebug() << "JasonQt_InvokeFromThread::waitForInvoke: Target thread" << thread << "is not running!";  
  74.         return;  
  75.     }  
  76.   
  77.     g_mutex.lock();  
  78.   
  79.     auto it = g_helpers.find(thread);  
  80.   
  81.     if(it == g_helpers.end())  
  82.     {  
  83.         auto helper = new JasonQt_InvokeFromThreadHelper;  
  84.         helper->moveToThread(thread);  
  85.   
  86.         QObject::connect(thread, &QThread::finished, [=]()  
  87.         {  
  88.             g_mutex.lock();  
  89.   
  90.             auto it = g_helpers.find(thread);  
  91.             if(it != g_helpers.end())  
  92.             {  
  93.                 g_helpers.erase(it);  
  94.             }  
  95.   
  96.             g_mutex.unlock();  
  97.         });  
  98.   
  99.         g_helpers.insert( thread, helper );  
  100.         it = g_helpers.find(thread);  
  101.     }  
  102.   
  103.     it.value()->addCallback([&]()  
  104.     {  
  105.         g_mutex.unlock();  
  106.         callback();  
  107.     });  
  108.   
  109.     QMetaObject::invokeMethod(it.value(), "onRun", Qt::QueuedConnection);  
  110.   
  111.     g_mutex.lock();  
  112.     g_mutex.unlock();  
  113. }  


测试代码:

[cpp] view plain copy
 
  1. int main(int argc, char *argv[])  
  2. {  
  3.     QCoreApplication a(argc, argv);  
  4.   
  5.     qDebug() << "Main thread:" << QThread::currentThread();  
  6.   
  7.     constexpr auto threadCount = 5;  
  8.     QVector< QObject * > objects;  
  9.     QThreadPool threadPool;  
  10.   
  11.     objects.resize(threadCount);  
  12.     threadPool.setMaxThreadCount(threadCount);  
  13.   
  14.     for(auto i = 0; i < threadCount; i++)  
  15.     {  
  16.         QtConcurrent::run([&objects, i]()  
  17.         {  
  18.             objects[i] = new QObject;  
  19.   
  20.             qDebug() << "Thread started:" << QThread::currentThread();  
  21.   
  22.             QEventLoop().exec();  
  23.         });  
  24.     }  
  25.   
  26.     // 等待测试线程启动  
  27.     QThread::sleep(3);  
  28.   
  29.     // 调用  
  30.     for(auto i = 0; i < (threadCount * 2); i++)  
  31.     {  
  32.         // 第一个参数是目标线程,第二个是回调  
  33.         JasonQt_InvokeFromThread::invoke(objects[i % threadCount]->thread(), [=]()  
  34.         {  
  35.             qDebug() << "Current thread:" << QThread::currentThread() << ", Flag:" << i;  
  36.         });  
  37.     }  
  38.   
  39.     return a.exec();  
  40. }  


执行输出

[cpp] view plain copy
 
  1. Main thread: QThread(0x7f821951bf30)  
  2. Thread started: QThread(0x7f8219705ac0, name = "Thread (pooled)")  
  3. Thread started: QThread(0x7f8219705f90, name = "Thread (pooled)")  
  4. Thread started: QThread(0x7f82197055f0, name = "Thread (pooled)")  
  5. Thread started: QThread(0x7f8219705120, name = "Thread (pooled)")  
  6. Thread started: QThread(0x7f8219704950, name = "Thread (pooled)")  
  7. Current thread: QThread(0x7f8219704950, name = "Thread (pooled)") , Flag: 0  
  8. Current thread: QThread(0x7f8219705ac0, name = "Thread (pooled)") , Flag: 3  
  9. Current thread: QThread(0x7f8219705120, name = "Thread (pooled)") , Flag: 1  
  10. Current thread: QThread(0x7f82197055f0, name = "Thread (pooled)") , Flag: 2  
  11. Current thread: QThread(0x7f8219705f90, name = "Thread (pooled)") , Flag: 4  
  12. Current thread: QThread(0x7f8219704950, name = "Thread (pooled)") , Flag: 5  
  13. Current thread: QThread(0x7f8219705ac0, name = "Thread (pooled)") , Flag: 8  
  14. Current thread: QThread(0x7f8219705120, name = "Thread (pooled)") , Flag: 6  
  15. Current thread: QThread(0x7f8219705f90, name = "Thread (pooled)") , Flag: 9  
  16. Current thread: QThread(0x7f82197055f0, name = "Thread (pooled)") , Flag: 7  


可以看见,回调被执行在了测试线程中。

注:目标线程需要有运行Qt的事件循环,这是必须的。

http://blog.csdn.net/wsj18808050/article/details/49950871

原文地址:https://www.cnblogs.com/findumars/p/5342184.html