写一个活着的线程
Writing a thread that stays alive
我想编写一个围绕std :: thread的类,表现得像std ::螺纹,但实际上每次我需要处理某些东西时都没有分配线程。原因是我需要在不允许动态分配的上下文中使用多线程,而且我也不想拥有创建std :: thread的开销。
相反,我希望一个线程在循环中运行并等到它可以开始处理。客户端调用invoke
唤醒线程。线程锁定了一个静音,进行了处理,然后再次入睡。一个函数join
的行为类似于std :: thread ::通过锁定直到线释放锁(即再次入睡)。
我认为我要参加课程,但是由于多线程缺乏经验,我想问问有人是否可以发现比赛条件,或者我使用的方法是否被认为是"良好的风格"。例如,我不确定临时锁定互斥X是否是"加入"线程的一种体面方法。
编辑我发现了另一个种族条件:当invoke
之后直接调用join
时,没有理由该线程已经锁定了Mutex,从而锁定join
的呼叫者,直到线程入睡为止。为了防止这种情况,我不得不添加一次调用计数器的支票。
标题
#pragma once
#include <thread>
#include <atomic>
#include <mutex>
class PersistentThread
{
public:
PersistentThread();
~PersistentThread();
// set function to invoke
// locks if thread is currently processing _func
void set(const std::function<void()> &f);
// wakes the thread up to process _func and fall asleep again
// locks if thread is currently processing _func
void invoke();
// mimics std::thread::join
// locks until the thread is finished with it's loop
void join();
private:
// intern thread loop
void loop(bool *initialized);
private:
bool _shutdownRequested{ false };
std::mutex _mutex;
std::unique_ptr<std::thread> _thread;
std::condition_variable _cond;
std::function<void()> _func{ nullptr };
};
源文件
#include "PersistentThread.h"
PersistentThread::PersistentThread()
{
auto lock = std::unique_lock<std::mutex>(_mutex);
bool initialized = false;
_thread = std::make_unique<std::thread>(&PersistentThread::loop, this, &initialized);
// wait until _thread notifies, check bool initialized to prevent spurious wakeups
_cond.wait(lock, [&] {return initialized; });
}
PersistentThread::~PersistentThread()
{
{
std::lock_guard<std::mutex> lock(_mutex);
_func = nullptr;
_shutdownRequested = true;
// wake up and let join
_cond.notify_one();
}
// join thread,
if (_thread->joinable())
{
_thread->join();
}
}
void PersistentThread::set(const std::function<void()>& f)
{
std::lock_guard<std::mutex> lock(_mutex);
this->_func = f;
}
void PersistentThread::invoke()
{
std::lock_guard<std::mutex> lock(_mutex);
_cond.notify_one();
}
void PersistentThread::join()
{
bool joined = false;
while (!joined)
{
std::lock_guard<std::mutex> lock(_mutex);
joined = (_invokeCounter == 0);
}
}
void PersistentThread::loop(bool *initialized)
{
std::unique_lock<std::mutex> lock(_mutex);
*initialized = true;
_cond.notify_one();
while (true)
{
// wait until we get the mutex again
_cond.wait(lock, [this] {return _shutdownRequested || (this->_invokeCounter > 0); });
// shut down if requested
if (_shutdownRequested) return;
// process
if (_func) _func();
_invokeCounter--;
}
}
您正在询问潜在的比赛条件,我在显示的代码中至少看到一个比赛条件。
构造PersistentThread
后,无法保证新线程将在主执行线程从构造函数返回并输入invoke()
之前在其loop()
中获取其初始锁定。构造函数完成后,主执行线程可能会立即输入invoke()
,最终通知任何人,因为内部执行线程尚未锁定MUTEX。因此,此invoke()
不会导致进行任何处理。
您需要将构造函数的完成与执行线程的初始锁定获取同步。
编辑:您的修订看起来正确;但是我也发现了另一个种族条件。
如Wait()描述中所记录的,wait()
可能醒来。仅仅因为wait()
返回,并不意味着其他一些线程已输入invoke()
。
除其他所有内容外,您还需要一个计数器,而invoke()
增加了计数器,并且仅在计数器大于零时执行其分配的职责,而执行线程则将其降低。这将防止虚假唤醒。
i还将执行线程在输入wait()
之前检查计数器,并仅在0时输入wait()
,否则,它会降低计数器,执行其函数并循环返回。p>这应该堵塞该区域的所有潜在种族条件。
P.S。伪造的唤醒还适用于您的校正中的初始通知,即执行线程已输入循环。您也需要在这种情况下做类似的事情。
我不明白您要准确地问什么。这是您使用的不错的样式。
使用bools
会更安全并检查单个routines
,因为void
毫无疑问,因此您可能会被错误造成。由于线程在引擎盖下运行,因此检查所有可能的东西。如果该过程真正成功,请确保通话正确。另外,您可以阅读一些有关"线程池"的内容。
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 异常属于C++中的线程还是进程
- C++中的线程安全删除
- C++使用params创建线程函数会导致转换错误
- 类与私有变量的其他类之间的线程安全性
- CoInitialize()在单独的线程上崩溃而不返回
- c++中的线程池
- 检查分离的线程是否还活着?
- 从多个线程组随机计算着着色器写入RWStructuredBuffer
- 写一个活着的线程
- Lua 关机双免费,关机期间 Lua 线程还活着?
- C++线程在kill之后仍然活着