在 C++ 中的线程内启动和停止循环

Posted

技术标签:

【中文标题】在 C++ 中的线程内启动和停止循环【英文标题】:Start and Stop a loop within a Thread in C++ 【发布时间】:2012-08-17 14:44:01 【问题描述】:

我需要从一个事件开始循环,然后从另一个事件停止它。 我的想法是当我按下按钮时调用函数 startDequeuing(),以便启动一个带有循环的线程,然后终止这个循环,将函数 stopDequeuing() 中的“出队”变量设置为 false。

这是我第一次使用线程,当我开始循环时程序会锁定,我认为是因为变量“出队”被锁定并且无法从线程外部访问,对吗?

我该如何解决这个问题??

这里有一些代码:

void CameraManager::startDequeuing()
    dequeuing = true;
    std::thread dequeueThread(&CameraManager::dequeueLoop, this);
    dequeueThread.join();


void CameraManager::stopDequeuing()
    dequeuing = false;


void *CameraManager::dequeueLoop()
    while(dequeuing)
        highSpeedCamera->dequeue();
        highSpeedCamera->enqueue();
    

【问题讨论】:

不,请不要使用 volatile,当它涉及到多线程时。它可能有效,但在正确编写的多线程应用程序中永远不需要它。这里的问题很可能是 stopDequeuing() 永远不会被调用,因为 startDequeing() 在 join() 上阻塞。 【参考方案1】:

使用线程的全部意义在于让多个函数并行运行。这里:

std::thread dequeueThread(&CameraManager::dequeueLoop, this);
dequeueThread.join();

您启动第二个线程并让第一个线程进入睡眠状态,等待生成的线程返回。所以你仍然只有一个线程在运行。如果您有这种 GUI 事件循环,您可能会锁定添加回调的可能性,当该事件循环为空时,该回调将被调用。这可能使您可以在不使用线程的情况下做您想做的事情。

解决方案可能如下所示:

void CameraManager::startDequeuing()
    dequeuing = true;
    dequeueThread = std::thread(&CameraManager::dequeueLoop, this);


void CameraManager::stopDequeuing()
    
        std::lock_guard<std::mutex> lock( mutex );
        dequeuing = false;
    
    dequeueThread.join();


bool CameraManager::keepOnDequeuing()

    std::lock_guard<std::mutex> lock( mutex );
    return dequeuing;


void *CameraManager::dequeueLoop()
    while( keepOnDequeuing() )
        highSpeedCamera->dequeue();
        highSpeedCamera->enqueue();
    

【讨论】:

您在startDequeing 中缺少一个锁 - 如果您在startstop 中有锁,为什么要为keepOnDequeuing 添加keepOnDequeuing justdequeueLoop而不是那里的另一个锁?另外,您的线程仍然在startDequeuing 的范围内?! @Dave 在启动线程之前没有锁定互斥锁是可以的,因为线程保证在线程启动之前看到对内存所做的更改。我添加了 keepOnDequeuing() 来制作 mutex.lock();读取标志; mutex.unlock() 更具可读性。尝试在没有那个帮助器的情况下编写 dequeueLoop() ,你会明白我的意思;-) lock_guard 将互斥锁锁定在它的 c'tor 中并在它的 d'tor 中解锁它,所以当名为 lock 的局部变量超出范围时,互斥锁已解锁()。 如果您不创建一个立即死亡的线程,您将需要互斥锁。而且,我知道 lock_guard 做什么,我没有问。 @Dave,没有创建线程,立即死亡。临时线程变量只是意味着你不能再指向那个线程,所以你不能 join() 例如。但是你是对的,dequeueThread 必须有一个成员变量,并且启动线程应该是 `dequeueThread = std::thread(&CameraManager::dequeueLoop, this);' 谢谢大家!我首先实现了 Torsten 提出的解决方案,当然将“dequeueThread”声明为一个类变量,并且效果很好!现在正如 Pete Becker 所建议的那样,我正在尝试使用 atomic bool 来提高性能。【参考方案2】:

你的程序死锁是因为join() 会阻塞直到你的线程函数完成;而且它永远不会在那个时候完成,因为它正在有效地执行while(true)

您希望dequeueThread 成为您班级的成员。为什么你希望它只在startDequeuing 的范围内存活?

【讨论】:

【参考方案3】:

dequeing 定义为原子布尔值:

#include <atomic>
std::atomic_bool dequeing = false;

它比使用互斥锁要快得多,并且可以获得相同的同步。

【讨论】:

忘了说:std::atomic_bool也可以拼成std::atomic&lt;bool&gt;

以上是关于在 C++ 中的线程内启动和停止循环的主要内容,如果未能解决你的问题,请参考以下文章

JMter随记

从分离线程停止主游戏循环的最佳方法

Python中级精华-并发之启动和停止线程

C++ asio 提供线程的异步执行

c++ 中的多线程线程安全动画建议

C++多线程编程,有没有方法让线程内的一些连续的语句一次执行完,在跳到其它的线程?