如何知道分离的 std::thread 是不是已完成执行?

Posted

技术标签:

【中文标题】如何知道分离的 std::thread 是不是已完成执行?【英文标题】:How to know whether detached std::thread has finished its execution?如何知道分离的 std::thread 是否已完成执行? 【发布时间】:2019-02-18 15:22:57 【问题描述】:

我有一个类似下面的函数,其中线程通过使用 std::lock_guard 互斥锁获取锁并通过 ofstream 写入文件。

当当前文件大小增加最大大小时,我创建一个独立线程来压缩文件并终止。

如果日志文件很大(比如 ~500MB),压缩大约需要 25 秒以上。 我分离了压缩线程,因为没有其他线程(或主线程)想要等待这个线程完成。

但我需要知道压缩线程在执行以下行之前没有运行:

_compress_thread(compress_log, _logfile).detach();

示例代码sn-p:

    void log (std::string message)
    
        // Lock using mutex
        std::lock_guard<std::mutex> lck(mtx);

        _outputFile << message << std::endl;
        _outputFile.flush();
        _sequence_number++;
        _curr_file_size = _outputFile.tellp();

        if (_curr_file_size >= max_size) 
            // Code to close the file stream, rename the file, and reopen
            ...


            // Create an independent thread to compress the file since
            // it takes some time to compress huge files.
            if (the_compress_thread_is_not_already_running) //pseudo code
            
                _compress_thread(compress_log, _logfile).detach();
            
        
    

在上述if 条件下,即the_compress_thread_is_not_already_running,如何确定压缩线程没有运行?

void * compress_log (std::string s) 


    // Compress the file
    // ...


【问题讨论】:

如果您需要知道线程是否仍在运行,那么听起来您并不真正想要detachdetach 通常表示设计存在问题。语句“我需要知道压缩线程没有运行”意味着应该有1个压缩线程,这与使用detach相反,并且与语句“没有其他线程”相反(或主)想要等待这个线程完成”. 您也可以使用std::async 并保留一个未来的对象。然后你可以使用它来确保在你再次调用之前之前的调用已经结束。 为什么会有问题? std::thread _compress_thread(compress_log, _logfile); 是一个局部变量,对您的函数来说是局部变量,并且总是创建一个新对象。它从不引用现有线程。 @C_user5 然后detach 与您想要做的完全相反。 join 在您为其分配新值之前使用它。 detach 的意思是“我不关心这个线程,它什么时候完成,或者即使它完成”,这在你的情况下是不正确的。编辑:但是正如 cmets 和答案所指出的那样,最好使用 std::async 并存储 std::future 来代替。 std::thread 旨在用作低级基本对象。它的设计不是为了易于使用。如果您想异步运行任务(压缩文件),请使用std::async。这就是它的用途。 【参考方案1】:

无法检测分离的执行线程是否已终止。

如果你出于某种原因需要保证最多一个线程同时压缩,那么一个简单的解决方案是使用std::async。它返回一个 future 对象。您可以查询未来对象关联的回调是否已完成。通过在函数末尾修改共享变量(注意共享访问必须同步),使用分离线程可以以较少结构化的方式实现相同的效果。

另一种方法是不断保持压缩线程的活动状态,但只要没有工作要做就阻止它。可以使用条件变量通知线程开始其工作,一旦完成,继续阻塞直到下一次通知。

附:您可能希望先关闭文件流,重命名文件,然后在您持有锁时重新打开,以便其他线程可以在之前的日志(现在在重命名的文件中)被压缩时继续登录到新文件。

【讨论】:

谢谢。在原始代码中,我已经在做以下事情:“关闭文件流,重命名文件,然后重新打开”。为了便于阅读,将它们从代码 sn-p 中删除。 对于“通过在函数末尾修改共享变量来使用分离线程(注意共享访问必须同步)”的方法,假设我使用共享变量并使用相同的保护它互斥体,即std::lock_guard&lt;std::mutex&gt; lck(mtx),仍然有可能正在清理压缩线程(在修改共享变量之后),而另一个线程最终执行_compress_thread(compress_log, _logfile).detach();。不是吗。。 @C_user5 我不会使用相同的互斥锁。我建议改用原子变量。也就是说,我首先不推荐这种方法,因为std::async 更简单。 still there is a possibility that the compress thread's clean up is in progress 当然。同样适用于使用std::async。如果这对您来说是个问题,那么恒定压缩线程方法可能对您有好处。也就是说,我不明白为什么会出现问题。

以上是关于如何知道分离的 std::thread 是不是已完成执行?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 QThread 创建一个分离的线程,就像在 std::thread 中一样

如何获取当前线程的 std::thread?

linux C++ std::thread::detach()函数(线程分离)

父线程离开其块后分离的 std::thread 的情况

如何终止或停止 C++ 中的分离线程?

C++11 的 std::thread 是不是与 POSIX 信号量兼容?