使用条件变量和原子布尔值停止 C++ 线程时遇到困难 [已关闭]

Difficulty halting C++ threads using conditional variables and atomic bools [closed]

提问人:caccolona 提问时间:11/13/2023 最后编辑:caccolona 更新时间:11/13/2023 访问量:61

问:


编辑问题以包括所需的行为、特定问题或错误以及重现问题所需的最短代码。这将帮助其他人回答这个问题。

8天前关闭。

我在应用程序中停止 C++ 线程时遇到问题。线程函数的核心部分涉及互斥锁和条件变量的使用:

class Acqer {
    std::atomic_bool running_;
    std::atomic_bool acquiring_;
    std::mutex dataMutex;
    std::condition_variable acqCV;
    std::condition_variable dataCV;

    std::jthread acqThread;
    std::jthread procThread;
    
public:
    void AcquireData();
    void ProcessData();
    void StartThreads();
    void StopThreads();
};

void Acqer::AcquireData() {
    while (running_)
    {
        // ...

        // Lock the mutex
        unique_lock<mutex> lock(dataMutex);
        // Use a condition variable to start the acquisition
        acqCV.wait(lock, [this]() -> bool
                   { return acquiring_; });

        // ...

        // Unlock the mutex
        lock.unlock();

        // Notify ProcessData thread
        dataCV.notify_one();

        // ...
    }
}

void Acqer::ProcessData() {
    while (running_)
    {
        unique_lock<mutex> lock(dataMutex);
        dataCV.wait(lock);

        // ...

        // Unlock the mutex
        lock.unlock();
    }
}

void Acqer::StartThreads()
{
    running_ = true;
    cout << "Starting acquisition and processing threads..." << endl;
    acqThread_ = jthread(&Acqer::AcquireData, this);
    procThread_ = jthread(&Acqer::ProcessData, this);
    cout << "Threads started." << endl;
}

void Acqer::StopThreads()
{
    running_ = false;
    acqCV.notify_all();
    dataCV.notify_all();
}

尽管使用原子布尔值 running_acquiring_ 以及互斥锁和条件变量,但即使在调用 StopThreads 函数后,线程 AcquireData 仍然存在。 我尝试调试代码,并且我注意到只有在采集尚未开始时才能停止两个线程(acqCV 仍在等待)。

当我将 running_ 设置为 false 时,AcquireData 线程似乎也没有到达 acqCV.wait(...) 行,但仍然不可联接。

是否存在可能阻止这些线程停止的潜在问题或注意事项,即使在信号终止后也是如此?

C++ 多线程

评论


答:

3赞 Mike Vine 11/13/2023 #1

您正在使用 的谓词形式。这意味着即使应该唤醒线程,如果谓词返回 false,它也会立即再次等待。wait

当你停止线程时,你会唤醒线程,但它会立即再次出现(大概?)false。您还需要签入谓词。请改为执行此操作:AcquireDatawaitacquiring__running

acqCV.wait(lock, [this]() -> bool
                   { return acquiring_ || !running_; });
if (!_running) break;

评论

0赞 caccolona 11/13/2023
第一部分实际上是我希望线程具有的行为:当 running_ 和 acquiring_ 为 true 时运行 while 循环,同时能够通过将 acquiring_ 设置为 false 来“暂停”它们。AcquireData 似乎在到达 acqCV.wait 行之前就挂起了(虽然不可加入)。
0赞 Mike Vine 11/13/2023
没关系。这仍然会这样做。通过我的更改,当运行设置为 false 时,它还会停止等待并终止循环。这大概是你想要的。否则你的问题就没有意义了?
0赞 caccolona 11/13/2023
我已经添加了这些行,但它仍然不起作用。当我将 AcquireData 设置为 false 时,running_似乎没有到达 acqCV.wait(...) 行,但似乎仍然不可联接。
0赞 Mike Vine 11/13/2023
在这种情况下,需要进行调试。进入你认为你将能够加入的情况,并使用调试器检查这些线程的调用堆栈,以查看它们现在被阻止的确切位置。
0赞 Mike Vine 11/13/2023
我唯一能看到的另一件事可能是一个问题,您可能处于尝试通过析构函数加入线程的情况。这会很糟糕。Acqer