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

时间:2016-03-01 09:26:24

标签: 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;
}

线程界面:

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);
}

TestExecution:

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());
    }
}

有人可以帮助我吗?

1 个答案:

答案 0 :(得分:3)

除非你想改变QThread作为线程工具/类的方式然后不对它进行子类化,否则使用moveToThread。以下是使用moveToThread()的一些注意事项:

QThread说明:

了解QThreads的工作原理非常重要。使用QThreads的一般程序是:

  • 让对象进入线程,不指定父级
  • 制作主题
  • 使用obj-&gt; moveToThread(thread)
  • 将对象移动到thead中
  • 将信号连接到对象中的插槽,该插槽将实例化对象成员(如果需要)
  • 启动主题:thread-&gt; 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()函数中的线程。

链接到有用的主题指南

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());
    }
}