第二个线程永远不会被触发

时间:2021-03-03 20:08:41

标签: c++ multithreading

我一直在努力解决多线程问题。我已经编写了一些简单的代码来尝试隔离问题,但我没有找到它。发生的事情是第一个线程被发送到它的数据唤醒,但第二个线程从未这样做。他们每个人都有自己的 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=%d\n", 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=0\n");
    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 RECEIVED\n");

        lck.unlock();
    }
    pthread_exit(nullptr);
}

void *custom_thread_1 (void *pVoid) {
    g_print("Created thread for source id=1\n");
    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 RECEIVED\n");

        lck.unlock();
    }
    pthread_exit(nullptr);
}

这是输出。如您所见,data_update 函数从源 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++ 线程。

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

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

当条件变量的谓词值在 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 可能是无状态的。