ITKeyword,专注技术干货聚合推荐

注册 | 登录

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

wsj18808050 分享于 2015-11-20

推荐:Qt 多线程编程中的对象线程与函数执行线程

近来用Qt编写一段多线程的TcpSocket通信程序,被其中Qt中报的几个warning搞晕了,一会儿是说“Cannot create children for a parent that is in a different thr

2020腾讯云8月秒杀活动,优惠非常大!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1040

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

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


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

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


两个主要接口

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

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


代码部分:


.h文件内容:

#include <QThread>
#include <QMap>
#include <QDebug>

class JasonQt_InvokeFromThreadHelper: public QObject
{
    Q_OBJECT

private:
    QMutex m_mutex;
    QList< std::function<void()> > m_waitCallbacks;

public:
    void addCallback(const std::function<void()> &callback);

public slots:
    void onRun();
};

class JasonQt_InvokeFromThread
{
private:
    static QMutex g_mutex;
    static QMap< QThread *, JasonQt_InvokeFromThreadHelper * > g_helpers;

public:
    static void invoke(QThread *thread, const std::function<void()> &callback);

    static void waitForInvoke(QThread *thread, const std::function<void()> &callback);
};

.cpp文件内容

// JasonQt_InvokeFromThreadHelper
void JasonQt_InvokeFromThreadHelper::addCallback(const std::function<void ()> &callback)
{
    m_mutex.lock();
    m_waitCallbacks.push_back(callback);
    m_mutex.unlock();
}

void JasonQt_InvokeFromThreadHelper::onRun()
{
    if(!m_waitCallbacks.isEmpty())
    {
        m_mutex.lock();

        auto callback = m_waitCallbacks.first();
        m_waitCallbacks.pop_front();

        m_mutex.unlock();

        callback();
    }
}

// JasonQt_InvokeFromThread
QMutex JasonQt_InvokeFromThread::g_mutex;
QMap< QThread *, JasonQt_InvokeFromThreadHelper * > JasonQt_InvokeFromThread::g_helpers;

void JasonQt_InvokeFromThread::invoke(QThread *thread, const std::function<void ()> &callback)
{
    if(!(thread->isRunning()))
    {
        qDebug() << "JasonQt_InvokeFromThread::invoke: Target thread" << thread << "is not running!";
        return;
    }

    g_mutex.lock();

    auto it = g_helpers.find(thread);

    if(it == g_helpers.end())
    {
        auto helper = new JasonQt_InvokeFromThreadHelper;
        helper->moveToThread(thread);

        QObject::connect(thread, &QThread::finished, [=]()
        {
            g_mutex.lock();

            auto it = g_helpers.find(thread);
            if(it != g_helpers.end())
            {
                g_helpers.erase(it);
            }

            g_mutex.unlock();
        });

        g_helpers.insert( thread, helper );
        it = g_helpers.find(thread);
    }

    it.value()->addCallback(callback);

    QMetaObject::invokeMethod(it.value(), "onRun", Qt::QueuedConnection);

    g_mutex.unlock();
}

void JasonQt_InvokeFromThread::waitForInvoke(QThread *thread, const std::function<void ()> &callback)
{
    if(!(thread->isRunning()))
    {
        qDebug() << "JasonQt_InvokeFromThread::waitForInvoke: Target thread" << thread << "is not running!";
        return;
    }

    g_mutex.lock();

    auto it = g_helpers.find(thread);

    if(it == g_helpers.end())
    {
        auto helper = new JasonQt_InvokeFromThreadHelper;
        helper->moveToThread(thread);

        QObject::connect(thread, &QThread::finished, [=]()
        {
            g_mutex.lock();

            auto it = g_helpers.find(thread);
            if(it != g_helpers.end())
            {
                g_helpers.erase(it);
            }

            g_mutex.unlock();
        });

        g_helpers.insert( thread, helper );
        it = g_helpers.find(thread);
    }

    it.value()->addCallback([&]()
    {
        g_mutex.unlock();
        callback();
    });

    QMetaObject::invokeMethod(it.value(), "onRun", Qt::QueuedConnection);

    g_mutex.lock();
    g_mutex.unlock();
}

测试代码:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "Main thread:" << QThread::currentThread();

    constexpr auto threadCount = 5;
    QVector< QObject * > objects;
    QThreadPool threadPool;

    objects.resize(threadCount);
    threadPool.setMaxThreadCount(threadCount);

    for(auto i = 0; i < threadCount; i++)
    {
        QtConcurrent::run([&objects, i]()
        {
            objects[i] = new QObject;

            qDebug() << "Thread started:" << QThread::currentThread();

            QEventLoop().exec();
        });
    }

    // 等待测试线程启动
    QThread::sleep(3);

    // 调用
    for(auto i = 0; i < (threadCount * 2); i++)
    {
        // 第一个参数是目标线程,第二个是回调
        JasonQt_InvokeFromThread::invoke(objects[i % threadCount]->thread(), [=]()
        {
            qDebug() << "Current thread:" << QThread::currentThread() << ", Flag:" << i;
        });
    }

    return a.exec();
}

执行输出

Main thread: QThread(0x7f821951bf30)
Thread started: QThread(0x7f8219705ac0, name = "Thread (pooled)")
Thread started: QThread(0x7f8219705f90, name = "Thread (pooled)")
Thread started: QThread(0x7f82197055f0, name = "Thread (pooled)")
Thread started: QThread(0x7f8219705120, name = "Thread (pooled)")
Thread started: QThread(0x7f8219704950, name = "Thread (pooled)")
Current thread: QThread(0x7f8219704950, name = "Thread (pooled)") , Flag: 0
Current thread: QThread(0x7f8219705ac0, name = "Thread (pooled)") , Flag: 3
Current thread: QThread(0x7f8219705120, name = "Thread (pooled)") , Flag: 1
Current thread: QThread(0x7f82197055f0, name = "Thread (pooled)") , Flag: 2
Current thread: QThread(0x7f8219705f90, name = "Thread (pooled)") , Flag: 4
Current thread: QThread(0x7f8219704950, name = "Thread (pooled)") , Flag: 5
Current thread: QThread(0x7f8219705ac0, name = "Thread (pooled)") , Flag: 8
Current thread: QThread(0x7f8219705120, name = "Thread (pooled)") , Flag: 6
Current thread: QThread(0x7f8219705f90, name = "Thread (pooled)") , Flag: 9
Current thread: QThread(0x7f82197055f0, name = "Thread (pooled)") , Flag: 7

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


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



推荐:Cocos2d-x 3.0 JNI BUG 修复。(Android 如何创建一个线程 延迟执行函数 创建一个随机数)

cocos2d-x 3.0bata 版本在 Cocos2dxActivity 的create方法下面直接访问c++的jni方法会崩溃。原因是cocos2d-x部分完全构建好需要一些延迟时间,所以java访问不到c

本类主要功能是在当前线程(比如说主线程),指派任意一个线程(比如说某个工作线程)去执行一个匿名函数。 注意,这个和QtConcurrent配合QThreadPool不一样,QtConcurrent配合QThreadPool只能

相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。