如何用阻塞调用控制线程的执行

How to control the execution of a thread with blocking call

本文关键字:线程 执行 控制线 控制 何用阻 调用      更新时间:2023-10-16

很抱歉,我找不到一个能清楚描述问题的标题。

请阅读下面的片段。有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;   
}

从不可分割的事务角度来考虑。

//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>顺序一致

虽然更精确的答案需要更多关于isRunning(), kill()join()的细节,但这里有一个明显的设计缺陷。

也就是说,m_running标志的目的是重载。

m_running这样的标志通常实现两种经典设计模式之一:

  1. 报告线程的状态:是否正在运行。

或:

  • 控制当前线程是运行还是停止。
  • 所示代码试图将两种设计模式合并在一起。而且它们不相交。

    如果m_running的目的是让有问题的线程报告其当前状态,那么线程1的stop()没有业务设置此标志。m_running应该只由所讨论的线程更新,并且,实际上,您的线程的run()在开始时将其设置为true,在返回之前将其设置为false。但是,run()本身没有必要检查if语句中m_running的值。它的唯一目的是报告线程的状态。不让线程做任何事情。

    在这种设计模式下,kill()通常实现了一些终止线程执行的方法,通常是通过向线程发送某种类型的消息来告诉线程停止执行。然后kill()将监视并等待,直到线程服从,并将m_running设置为false,表示它退出。或者,如果这是一个可接合的线程,kill()将加入该线程。

    或者,m_running的目的可以是通知线程停止它正在做的事情。但是在这种情况下,执行线程的run()没有必要更新m_running本身。在这种设计模式中,线程通常所做的就是执行while(m_running)循环,等待下一个消息。停止线程的典型过程包括设置m_running,然后向线程发送某种no-op消息——该消息的唯一目的是唤醒线程,以便它可以通过循环并查看m_running的更新值。

    所示代码试图将两种设计模式合并为一个变量,在我看来,这是设计问题的根本原因。

    m_running分离为两个独立的变量:一个用于报告线程的状态(如果需要的话),另一个变量用作有序关闭线程的可靠机制的一部分。