c++ - 使用 QThreads 与多个硬件设备通信

标签 c++ multithreading qt qt5

我编写了一个与多个硬件设备通信的 C++ 工具(相同的代码 -> 线程函数,但多个设备)

我目前的方法是使用 Winapi 线程机制 beginthreadex()WaitForMultipleObjects()(有效)

int vectorIndex = 0;
for(int deviceCounter=0; deviceCounter<GetNumberOfDevices(); deviceCounter++)
{                                           
    // create thread DATA object                
    testFlowVector.push_back(std::make_shared<TestFlow>(m_testResultsVector, m_logFiles));
    testFlowVector.at(vectorIndex)->SetThreadID(deviceCounter); // threadID is here the number to the device network socket


    // CREATE THREADS       
    m_threadHandle->at(deviceCounter) = (HANDLE)_beginthreadex(0, 0, TestExecution::ExecTestFlow, testFlowVector.at(vectorIndex).get(), 0, 0);

    vectorIndex++;
}           

// wait for all threads         
DWORD waitReturn = WaitForMultipleObjects(m_tpgmParam->GetNumberOfReader(), m_threadHandle->data(), true, m_tpgmParam->GetThreadTimeout()); 

静态线程函数:

unsigned int __stdcall TestExecution::ExecTestFlow(void *param)
{   
    TestFlowInterface *testFlowInterface = static_cast<TestFlowInterface*>(param);      
    testFlowInterface->run();   

    return 999;
}

线程接口(interface):

class TestFlowInterface
{
    virtual void ExecuteTestflow() = 0;

public:
    void run() { ExecuteTestflow(); } 
    virtual ~TestFlowInterface() {} 
};

测试流类:

class TestFlow : public TestFlowInterface
{
public:
    TestFlow(std::shared_ptr<std::vector<int>> testResults, std::shared_ptr<LogFiles> logFiles);    
    ~TestFlow();

    void ExecuteTestflow();
    // ...
};

我将 Qt5 用于 GUI、网络通信等,并且还想根据此文档修改我的代码以使用 QThreads:http://doc.qt.io/qt-5/qthread.html

有两种解决方案

  • 子类 QThread
  • 使用moveToThread()(推荐)

我都试过了,但我无法改变它。

--- 编辑:---

TestFlow 类

class TestFlow : public QObject
{
    Q_OBJECT

public:
    TestFlow(std::shared_ptr<LogFiles> logFiles, QObjects *parent=0);   
    ~TestFlow();

    void ExecuteTestflow();
    // ...

private:
    QMutex m_mutex;
    std::vector<int> m_testResults;

signals:
    void ResultReady(const std::vector<int> &result);

public slots:
    void DoWork();

};

void TestFlow::DoWork()
{
    QMutexLocker mutexLocker(m_mutex);
    ExecuteTestflow();

    emit ResultReady(m_testResults);
}

测试执行:

class TestExecution : public QObject
{
    Q_OBJECT

public:
    TestExecution()
    {
        m_workerThread = new QThread();
    }
    ~TestExecution();

private:
    QThread *m_workerThread;

public slots:
    void HandleTestResult(const std::vector<int> &result)
    {
        // do something with result
    }

};

void TestExecution::StartTest()
{
    for(int deviceCounter=0; deviceCounter<GetNumberOfDevices(); deviceCounter++)
    {
        TestFlow *testFlow = new TestFlow();
        testFlow->SetThreadID(deviceCounter);

        connect(testFlow, &TestFlow::ResultReady, this, &TestExecution::HandleTestResult);
        connect(m_workerThread, &QThread::started, testFlow, &TestFlow::DoWork);
        testFlow->moveToThread(m_workerThread);

        m_workerThread->start();
        m_workerThread->wait(m_tpgmParam->GetThreadTimeout());
    }
}

有人能帮帮我吗?

最佳答案

除非您想更改 QThread 作为线程实用程序/类的工作方式,否则不要将其子类化,请使用 moveToThread。以下是使用 moveToThread() 的一些注意事项:

QThread 注释:

了解 QThreads 的工作原理很重要。使用 QThreads 的一般过程是:

  • 让对象进入线程,不分配父对象
  • 发帖
  • 使用 obj->moveToThread(thread) 将对象移动到线程中
  • 将信号连接到将实例化对象成员的对象中的槽(如果需要)
  • 启动线程:thread->start()

现在,一旦对象接收到实例化其成员的信号,它们就会 在线程中实例化。

注意:如果您直接从实例化对象成员的线程外部(或从另一个线程)调用对象方法,那么这些对象实际上将从线程外部创建,您可能会得到警告说这样的话: “定时器不能从另一个线程启动” 即成员函数不是线程安全的。

// Good example:
MyObj myObj = new MyObj(0); // 0 = no parent
QThread* thread = new QThread;
myObj->moveToThread(thread);
QObject::connect(thread, SIGNAL(started()), myObj, SLOT(run()));
thread->start();

// Bad example:
MyObj myObj = new MyObj(0); // 0 = no parent
QThread* thread = new QThread;
myObj->moveToThread(thread);
thread->start();
myObj->run(); //BAD - anything run() instantiates will be in 'this' thread - i.e. never call functions to this class directly once it has been moved to the other thread.

对于您的代码,您只需创建一个类/对象,在其中运行您的线程/硬件通信代码并实现 run() 槽函数。然后你可以使用上面的例子来设置它。

确保你的“worker”类是一个 QOBject 类(如果你在 Qt Creator 向导下创建类,Qt 可以为你做这件事,确保你添加继承自 QObject)。您需要这个以便您可以在类之间使用插槽/信号接口(interface)。

编辑因为代码已发布。

删除 m_workerThread->wait(m_tpgmParam->GetThreadTimeout());,因为它阻塞了事件队列,因此您从工作线程的回复无法通过。

您将需要转向更多事件驱动的设计,因为像“在这里等到”这样的事情与 qt 插槽/信号一起并不是很好的设计(即,这不是您打算使用它的方式)。

但是如果您确实想等待线程结束,那么您将必须确保您的dowork() 函数结束线程。我认为您需要使用 QThread::quit() 函数从 dowork() 函数结束线程。

有用线程指南的链接

see here

在这里看看工作线程如何发出连接到 QThread::quit() 槽的完成信号。这是结束您正在尝试执行的线程的最佳方式,您需要考虑一下等待的原因。

示例代码

signals:
    void ResultReady(const std::vector<int> &result);
    void quitThread(); // <------------- New signal

public slots:
    void DoWork();

};

void TestFlow::DoWork()
{
    QMutexLocker mutexLocker(m_mutex);
    ExecuteTestflow();

    // V------ Note this signal will NOT get processed until your wait function is finished! and it won't finish until the thread finishes (or your timeout occurs)...
    emit ResultReady(m_testResults);
    emit quitThread(); // <------------- Signal the thread can quit
}

-

void TestExecution::StartTest()
{
    for(int deviceCounter=0; deviceCounter<GetNumberOfDevices(); deviceCounter++)
    {
        TestFlow *testFlow = new TestFlow();
        testFlow->SetThreadID(deviceCounter);

        connect(testFlow, &TestFlow::ResultReady, this, &TestExecution::HandleTestResult);
        connect(m_workerThread, &QThread::started, testFlow, &TestFlow::DoWork);
        // V------------- Connect the quitThread signal to the quit slot of the thread
        //to end the thread once worker is signals it is done.
        connect(testFlow, &QThread::quitThread, m_workerThread, &QThread::quit);
        testFlow->moveToThread(m_workerThread);

        m_workerThread->start();

        /* V------------- 
           This should now get un-blocked once the thread ends since the
           quitThread signal goes directly to the thread and therefore is
           not blocked by this "wait" which IS blocking THIS thread from
           processing incoming signals. */
        m_workerThread->wait(m_tpgmParam->GetThreadTimeout());
    }
}

关于c++ - 使用 QThreads 与多个硬件设备通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35718991/

相关文章:

c++ - 是否有一个内置函数,其作用类似于从字符串开始的 strcat ?

c++ - 仅蚕食右值引用的部分资源

python - random.seed(seed) 是否在多个进程中生成相同的序列?

c++ - 与应用程序的链接错误

qt - 如何在 Qt TableView 中实现类似过滤的电子表格?

c++ - 返回一堆值的接口(interface)

c++ - RGB 到十六进制语法

c++ - 如何统计n个线程和fork()的创建和终止时间?

.net - 线程安全对象 - 静态还是非静态?

c++ - 在 OS X 上构建 Qt QIBASE 驱动程序