对不起标题,我找不到能够清楚地描述问题的那个。
请阅读下面的代码段。有2个线程,第一个控制执行,第二个完成工作。
std::atomic<bool> m_running;
Process* process; // A thread-safe process implementation
//thread 1
void stop()
{
m_running = false;
if( process->isRunning() )
process->kill(); // process->join() returns as a result of this call
}
//thread 2
void run()
{
m_running = true;
while( m_running )
{
do_something();
process->start();
process->join(); // Blocking call
}
m_running = false;
}
我注意到如果第一个线程在第二个线程忙于do_something()
时开始执行,它会将false
分配给m_running
并检查进程是否正在运行或不。由于第二个帖子仍在do_something()
调用中,process->isRunning()
将返回false
,第一个帖子将返回而不会终止该进程。
但是第二个帖子会在完成do_something()
后立即启动流程,而不是我们的停止需求。
我想到的第一件事就是如下:
//thread 2
void run()
{
m_running = true;
while( m_running )
{
do_something();
if( m_running )
{
// flagged line ...
process->start();
}
process->join(); // Blocking call
}
m_running = false;
}
但是当第二个线程在标记行中被中断时,线程1中的整个stop()
方法仍然可以开始和结束。
所以我决定使用互斥锁,但我不确定一切是否正常:
//thread 1
void stop()
{
mutex.lock();
m_running = false;
if( process->isRunning() )
process->kill(); // process->join() returns as a result of this call
mutex.unlock();
}
//thread 2
void run()
{
m_running = true;
while( m_running )
{
do_something();
mutex.lock();
if( m_running )
{
process->start();
}
mutex.unlock();
process->join(); // Blocking call
}
m_running = false;
}
答案 0 :(得分:1)
虽然更精确的答案需要有关isRunning()
,kill()
和join()
的详细信息,但这里有一个明显的设计缺陷。
即,m_running
标志的目的是重载。
像m_running
这样的标志通常实现两种经典设计模式之一:
或:
显示的代码试图将两种设计模式混合在一起。而且它们没有啮合。
如果m_running
的目的是让有问题的线程报告其当前状态,那么线程1的stop()
没有业务设置此标志。 m_running
只应由相关主题进行更新,实际上,您的主题run()
在开始时将其设置为true
,并在返回之前将其设置为false
。但是,run()
本身没有业务检查m_running
语句中if
的值。它的唯一目的是报告线程的状态。不要让线程做任何事情。
使用这种设计模式,kill()
通常实现一些终止线程执行的方法,通常是通过向线程发送某种消息,告诉线程结束。然后kill()
将监视并等待线程服从,并将m_running
设置为false,表示它已退出。或者,如果这是一个可连接的线程,kill()
将加入该线程。
或者,m_running
的目的可以是通知线程停止它正在做的事情。但在这种情况下,执行线程的run()
本身没有业务更新m_running
。所有线程通常在此设计模式中执行,执行while(m_running)
循环,等待下一条消息。停止线程的典型过程包括设置m_running
,然后向线程发送某种无操作消息 - 消息的唯一目的是唤醒线程,因此它可以循环遍历循环请参阅m_running
的更新值。
显示的代码试图将两种设计模式混合成一个变量,在我看来这就是设计问题的根本原因。
将m_running
分成两个离散变量:一个用于报告线程的状态(如果需要),另一个用于作为有序关闭线程的可靠机制的一部分。
答案 1 :(得分:1)
从不可分割的交易中考虑它。
//thread 1
void stop()
{
m_running = false;
{
//This block should happen as a transaction
std::lock_guard<std::mutex> lck(m_mutex);
if( process->isRunning() )
process->kill();
}
}
//thread 2
void run()
{
m_running = true;
while( m_running )
{
do_something();
{
//Since starting this process depends on m_running
//which could change anytime since we tested it last,
//we test it again, and start, this time as another transaction
std::lock_guard<std::mutex> lck(m_mutex);
if(m_running)
process->start();
}
process->join(); // Blocking call
}
}
您不需要为保护m_running
而烦恼,因为它的默认std::atomic<bool>
顺序一致