繁体   English   中英

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

[英]How to control the execution of a thread with blocking call

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

请阅读下面的代码段。 有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()时开始执行,它将为m_running分配false并检查进程是否正在运行。 由于第二个线程仍然在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;   
}

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

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

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

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

要么:

  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分成两个离散变量:一个用于报告线程的状态(如果需要),另一个用于作为线程有序关闭的可靠机制的一部分。

从不可分割的交易来看。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM