第二个线程永远不会触发



我一直在努力解决多线程问题。我写了一些简单的代码来尝试隔离问题,但我没有找到它。发生的情况是,第一个线程被唤醒,数据被发送给它,但第二个线程从未被唤醒。它们都有自己的condition_variable,但这似乎无关紧要。最终,我要做的是让几个长时间运行的线程在需要时执行单个专用任务,而在不需要时保持等待状态。在各自的线程中运行它们是很重要的,也是必要的。

代码如下:

#include <glib.h>
#include <string>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#define NUM_THREADS 2
bool DEBUG = true;
pthread_t threads[NUM_THREADS];
std::mutex m_0;
std::mutex m_1;
std::condition_variable cov_0;
std::condition_variable cov_1;
bool dataReady_0 = false;
bool dataReady_1 = false;
bool keepRunning[NUM_THREADS] = { true };
void date_update (guint source_id, const char *json_data) {
if (DEBUG) {
start_threads(2);
sleep(2);
DEBUG = false;
}
g_print("From source id=%dn", source_id);
switch (source_id) {
case 0:
dataReady_0 = true;
cov_0.notify_one();
break;
case 1:
dataReady_1 = true;
cov_1.notify_one();
break;
}
}
void start_threads (int thread_count) {
int rc;
switch (thread_count) {
case 2:
rc = pthread_create(&threads[1], nullptr, custom_thread_1, nullptr);
if (rc) {
g_print("Error:unable to create thread(1), return code(%d)n", rc);
}
case 1:
rc = pthread_create(&threads[0], nullptr, custom_thread_0, nullptr);
if (rc) {
g_print("Error:unable to create thread(0), return code(%d)n", rc);
}
}
}
void *custom_thread_0 (void *pVoid) {
g_print("Created thread for source id=0n");
while (keepRunning[0]) {
// Wait until date_update() sends data
std::unique_lock<std::mutex> lck(m_0);
cov_0.wait(lck, [&]{return dataReady_0;});
dataReady_0 = false;
g_print("THREAD=0, DATA RECEIVEDn");
lck.unlock();
}
pthread_exit(nullptr);
}
void *custom_thread_1 (void *pVoid) {
g_print("Created thread for source id=1n");
while (keepRunning[1]) {
// Wait until date_update() sends data
std::unique_lock<std::mutex> lck(m_1);
cov_1.wait(lck, [&]{return dataReady_1;});
dataReady_1 = false;
g_print("THREAD=1, DATA RECEIVEDn");
lck.unlock();
}
pthread_exit(nullptr);
}

输出如下。如您所见,data_update函数获取了"data"源0和源1的调用函数,但似乎只有线程0处理任何事情。关于问题的根源,我有点茫然。

Sending data for source id=1
From source id=1
Sending data for source id=0
From source id=0
THREAD=0, DATA RECEIVED
Sending data for source id=1
From source id=1
Sending data for source id=0
From source id=0
THREAD=0, DATA RECEIVED
Sending data for source id=1
From source id=1
Sending data for source id=0
From source id=0
THREAD=0, DATA RECEIVED

我相信我只是遗漏了一个小细节,但我完全愿意接受,也许我没有正确理解C/c++线程。

第二个线程退出,因为keepRunning状态标志为假。记录所有线程的启动和退出通常是调试线程的第一步。

但是你有一个不那么明显的问题。

date_update()中条件变量的谓词的值发生变化时,似乎没有保持合适的互斥锁。

我再详细解释一下。

当调用cov_0.wait()时,使用的谓词是[&]{return dataReady_0;}(*),传递的unique_lock持有互斥体m_0。这意味着无论谓词的值何时发生变化,互斥锁m_0都必须保持。

这个谓词非常简单,当全局变量dataReady_0改变值时,它的值也会改变。

date_update()中,有代码可以改变dataReady_0的值,并且在此过程中不持有互斥锁m_0。在块中应该有一个scoped_lockunique_lock来改变全局变量的状态。

如果没有这个,它仍然可以工作,但是你有一个比赛!它最终会失败的!

条件变量可以检查并看到谓词为假,然后另一个线程更改谓词的值并执行通知,然后第一个线程等待条件变量。它错过了通知,因为它在发送通知时还没有等待。使用互斥锁来防止谓词以与通知竞争的方式更改是实现此工作的关键组成部分。

(*)这里不需要捕获[&]。这个lambda可以是无状态的。

你应该初始化内置数组的所有元素:

bool keepRunning[2] = { true, true };

最新更新