Qt多线程通信 我是C++和QT的新手,在实现一个好的而不是过于复杂的多线程通信环境时,我遇到了一些麻烦。p>

Qt多线程通信 我是C++和QT的新手,在实现一个好的而不是过于复杂的多线程通信环境时,我遇到了一些麻烦。p>,c++,multithreading,qt,qthread,qtcore,C++,Multithreading,Qt,Qthread,Qtcore,基本上,我有3个线程,一个用于GUI,另一个用于处理通过USB连接到PC的设备发送的更新,另一个用于处理设备和控制设备和GUI获取的信息以更改其状态。所以基本上有3个线程:GUI、device和control 我的第一种方法是让device用USB发送的信息填充其私有成员,并使用一些get()方法转换并返回此数据(使用互斥锁确保数据仍然有效)。问题是,当control调用device中的get()方法时,它不会返回任何新的内容(我原以为这些方法甚至永远不会返回,因为线程在另一个方法中被锁定,但它

基本上,我有3个线程,一个用于
GUI
,另一个用于处理通过USB连接到PC的
设备发送的更新,另一个用于处理
设备和
控制
设备和
GUI
获取的信息以更改其状态。所以基本上有3个线程:
GUI
device
control

我的第一种方法是让
device
用USB发送的信息填充其私有成员,并使用一些
get()
方法转换并返回此数据(使用互斥锁确保数据仍然有效)。问题是,当
control
调用
device
中的
get()
方法时,它不会返回任何新的内容(我原以为这些方法甚至永远不会返回,因为线程在另一个方法中被锁定,但它们确实返回并且没有新信息,而且在那些
get()中也不会触发断点)
方法)

Qt进行线程间通信的通常方式是使用信号和插槽,但信号和插槽的问题是,当一个线程正在处理并且它有一个插槽时,如果发送了某个信号,则该插槽将永远不会执行。即使我能设法使用信号和插槽触发新的数据更新,我担心会有很多信号被发送,因为设备更新非常快,此外,我有很多数据类型,使用QAtomicInt对其中许多类型都没有用处,因此我的一般问题是,哪种方法是使线程共享数据并保持无限进程循环的最佳方法?

我的目标的一个很好的例子是:

控制线程:

while(true){
    angle = device.getAngle(); //device is a member of control object and is running in a separate thread
    doCalculations(angle);
}
设备线程:

void process(){
while(true)
    usbRead(data, size_of_data);
}

short getAngle(){
    return (data[0] << 8 | data[1]);
}

假设您的设备是这样工作的:

while(keep_running){
    handle_incoming_data();
}
在这个场景中,您没有时间处理这个线程中的任何其他内容,因为这个循环根本不会退出。但是,可以将循环更改为以下构造:

public slots:
    void single_step(){
        if(keep_running){
            handle_incoming_data();
            QTimer::singleShot(0, this, SLOT(single_step()));
        }
    }

其中,
my\u timer
是一个
QTimer*
并连接到正确的插槽(在这种情况下,
处理传入的\u数据()
。请注意,0毫秒超时具有特殊含义:

作为一种特殊情况,超时为0的
QTimer
将在处理完窗口系统事件队列中的所有事件后立即超时。这可用于完成繁重的工作,同时提供快速的用户界面:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();

有关更多信息,请参阅。

处理此问题的方法不止一种:

1) 手动睡眠和在某些时段自己醒来,检查是否有变化。用行话来说,这就是投票

2) 为线程使用事件循环来处理信号和插槽等事件

我建议使用后者,因为前者可能有一个缺点,就是在睡觉的时候不能做任何事情,你可能会弄错。更重要的是,您还将突然失去强大的信号和插槽机制,并且您的代码将变得更加耦合

至于正确执行后者,您需要确保适当的Qt事件循环执行到位,这是由更高版本的
QThread
保证的。尽管如此,请阅读以下文档,因为我认为了解以下内容很有用:

进入事件循环并等待直到调用exit(),返回传递给exit()的值。如果通过quit()调用exit(),则返回的值为0

此函数旨在从run()内部调用。必须调用此函数才能开始事件处理

最后,您还可以通过混合上述两种方法来实现中间的第三种解决方案,即:调用下面的方法以明确确保所有事件都得到处理。这将是一种半(a)同步方式(取决于您如何看待它),因为当您从睡眠中醒来进行轮询时需要这样做,但您可以使用这种方便的方法,而不是手动处理轮询

根据指定的标志处理调用线程的所有挂起事件,直到没有更多事件要处理

当程序忙于执行长操作(例如复制文件)时,可以偶尔调用此函数

如果您正在运行一个连续调用此函数的本地循环,而没有事件循环,则不会处理DeferredElete事件。这可能会影响依赖DeferredElete事件正常运行的小部件(如QToolTip)的行为。另一种方法是从该本地循环中调用sendPostedEvents()

调用此函数仅处理调用线程的事件


您可以通过
QTimer
调用每个迭代,而不是无限循环。这给了你足够的时间在信号和插槽之间。是的,但这听起来很复杂,例如,
设备
有18个参数正在更新,大多数情况下只需要2个或3个参数,因此每次有新数据向
控制发送信号
时,我会觉得数据流量太大……你怎么看?你已经在这里问过了,我们回答:请参考现有线程重新表述你的问题,等等。看起来你正试图通过几个小线程来解决一个任务,偶尔读者不知道其他部分。不管怎样,那边的答案怎么了?您可以按编写的方式执行事件循环,它将安排事件进行处理。另外,我建议在每小时或每秒钟发帖之前多读一些关于这些事情的内容。拉兹洛,请阅读问题
public slots:
    void start_work(){
        my_timer->start(0);
    }

    void stop_work(){
        my_timer->stop();
    }
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();