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



我写了一个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;
}

线程接口:

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

有2种解决方案

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

我尝试了两者,但我无法更改它。

---编辑:---

测试流类

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() 的一些注意事项:

Q阅读说明:

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

  • 使对象进入线程,不分配父项
  • 制作线程
  • 使用 obj->moveToThread(thread) 将对象移动到 thead 中
  • 信号连接到对象中的插槽,该插槽将激活对象成员(如果需要)
  • 启动线程: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添加继承)。您需要这个,以便您可以在类之间使用插槽/信号接口。

编辑 自从代码发布以来。

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

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

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

链接到有用的线程指南

看这里

在这里看看工作线程如何发出连接到 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());
    }
}

最新更新