C++ 包装std::thread以作为类成员重用
我希望我的类产生两个线程,它们将在循环中运行并与所有者类交互。这很容易,当我的类最初使用单线程时:一个原子标志、一个互斥等。但是随着对第二个线程的需求增加,我正在考虑一些更优雅的解决方案,它将封装线程及其实用程序。例如:ThreadWrapper类,可以在所有者类中实例化 到目前为止,我提出了以下建议,但我不确定这是否正确:C++ 包装std::thread以作为类成员重用,c++,multithreading,c++11,c++14,C++,Multithreading,C++11,C++14,我希望我的类产生两个线程,它们将在循环中运行并与所有者类交互。这很容易,当我的类最初使用单线程时:一个原子标志、一个互斥等。但是随着对第二个线程的需求增加,我正在考虑一些更优雅的解决方案,它将封装线程及其实用程序。例如:ThreadWrapper类,可以在所有者类中实例化 到目前为止,我提出了以下建议,但我不确定这是否正确: class ThreadWrapper { public: ThreadWrapper() : m_threadShouldRun(false)
class ThreadWrapper
{
public:
ThreadWrapper()
: m_threadShouldRun(false)
, m_threadRunning(false)
{
}
ThreadWrapper(std::function<void()> fn)
: m_threadShouldRun(true)
, m_threadRunning(false)
, m_threadFunction(fn)
{
m_threadPointer = std::make_shared<std::thread>(std::thread([&] { this->threadLoop(); }));
}
virtual ~ThreadWrapper()
{
if (m_threadRunning)
m_threadShouldRun = false;
m_threadPointer->join();
m_threadPointer = nullptr;
}
private:
void threadLoop()
{
m_threadRunning = true;
while(m_threadShouldRun)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
m_threadFunction();
}
m_threadRunning = false;
}
std::function<void()> m_threadFunction = []{};
std::shared_ptr<std::thread> m_threadPointer;
std::atomic<bool> m_threadShouldRun;
std::atomic<bool> m_threadRunning;
};
类线程包装器
{
公众:
ThreadWrapper()
:m_threadshouldlrun(false)
,m_threadRunning(假)
{
}
螺纹包装器(标准::函数fn)
:m_threadshouldlrun(true)
,m_threadRunning(假)
,m_螺纹功能(fn)
{
m_threadPointer=std::make_shared(std::thread([&]{this->threadLoop();}));
}
虚拟~ThreadWrapper()
{
如果(m_threadRunning)
m_threadShouldRun=false;
m_threadPointer->join();
m_threadPointer=nullptr;
}
私人:
void threadLoop()
{
m_threadRunning=真;
while(m_threadshouldlrun)
{
std::this_线程::sleep_for(std::chrono::毫秒(100));
m_threadFunction();
}
m_threadRunning=假;
}
std::函数m_threadFunction=[]{};
std::共享的线程指针;
std::原子m_线程应运行;
std::原子m_线程运行;
};
用法(固定):
class-Foo
{
Foo()
:t1(std::function(std::bind(&Foo::internalLoop1,this)))
,t2(std::function(std::bind(&Foo::internalLoop2,this)))
{
}
螺纹包装t1;
线状包装物t2;
std::互斥mtx;
void internalLoop1()
{
//循环任务
{
标准:范围锁定(mtx);
//向发送/接收队列写入和读取数据
}
}
void internalLoop2()
{
//循环任务
{
标准:范围锁定(mtx);
//处理接收队列
}
}
};
这有点像我发现的包装。
缺点是睡眠周期需要传递给ThreadWrapper对象
这是正确的方法吗?或者它应该用基类来完成,而只是继承虚拟方法?如下图所示。
资料来源:
理想情况下,我希望在这里使用一个模板,但是两个ThreadWrapper之间唯一“不同”的部分是一个方法,它建议我使用继承/函数指针。还是我错了
编辑:动机。在C#中,可以执行以下操作:
vart1=newthread(()=>internalLoop1())代码>最后,我在我的主课上处理这样一行程序。其中另一个ThreadWrapper类负责线程安全管理。因此,如果要生成多个线程,那么很容易复制。我很难想象std::shared\u ptr
是正确使用的场景。你真的希望你的包装是可复制的并且共享std::thread
对象吗?那么其他成员的这些新实例用于什么呢?每个标志都有两个实例、两个函数对象和一个使用一组这些成员的线程。如果原件被销毁,则捕获的this
将悬空。似乎std::unique\u ptr
会更好。但在这一点上,您也可以直接使用std::thread
成员来代替。您有一个busylop,它做一些事情,然后休眠,然后循环。远远少于0.1%的线程代码需要一个busylop,并且需要充分证明它的合理性。我同意@FrançoisAndrieux。对我来说更像是个XY问题。你能更详细地解释一下你最终想要实现/优化什么吗?这->internalLoop1
是胡说八道。代码的其余部分有很多拼写错误。请尽可能在解释问题时发布编译代码。不要过早地概括代码…实际上,您的解决方案很少可用,因为大多数问题都需要特定的解决方案。例如,您可能需要在特定点启动线程。在正确编写的代码中,几乎不需要有延迟的循环,而是希望等待某个事件。
class Foo
{
Foo()
: t1(std::function<void()>(std::bind(&Foo::internalLoop1, this)))
, t2(std::function<void()>(std::bind(&Foo::internalLoop2, this)))
{
}
ThreadWrapper t1;
ThreadWrapper t2;
std::mutex mtx;
void internalLoop1()
{
// looped task
{
std::scoped_lock lock(mtx);
// write'n'read data to tx/rx queues
}
}
void internalLoop2()
{
// looped task
{
std::scoped_lock lock(mtx);
// process the rx queue
}
}
};