我曾多次遇到以下情况(在 C++ 和 Python 中),我想知道一个好的(且正确的)方法来解决它:
我正在使用两个不同的库(所以我无法更改该代码),每个库都提供与这些类似的类:
// Call to do the "work"
class LongRunner
{
public:
void runForAwhile();
// Only interrupts runForAwhile() if it is currently running.
// Doesn't do anything otherwise (ie, can still be called)
void stopRunner();
};
// Override to define execution
class ExecutionBase
{
public:
virtual void executeInSeparateThread();
// Joins the execution thread. Can be overridden for additional cleanup.
virtual void halt();
};
我的派生类的非线程安全版本是:
class MyExecution : ExecutionBase
{
public:
void executeInSeparateThread() override
{
doBeforeThings();
long_runner_.runForAwhile();
doAfterThings();
}
void halt() override
{
long_runner_.stopRunner();
ExecutionBase::halt();
}
private:
LongRunner long_runner_;
};
库控制创建
executeInSeparateThread()
线程并在不同线程中调用 halt()
。我应该使用什么锁等来确保 halt()
始终正确地中断 executeInSeparateThread()
?
令我困惑的是,
stopRunner()
仅会中断 runForAwhile()
(如果它当前正在运行)。因此,一旦在stopRunner()
中调用halt()
,如果runForAwhile()
还没有启动,线程同步需要保证它永远不会启动。
这个问题没有必要,但如果你好奇的话,
LongRunner
真的是moveit::task_constructor::Task
(plan()
和preempt()
)而ExecutionBase
是BT::ThreadedAction
void runForAwhile();
void stopRunner(); // Only interrupts runForAwhile() if it is currently running.
如果
stopRunner
在 runForAwhile
之前被调用怎么办?
任何“好的”设计仍然会停止
runForAwhile
如果首先调用stopRunner
,这就是链接库所做的,它就像检查原子布尔一样简单。
std::atomic<bool> run = true;
// run = false; !!!!! BAD DESIGN !!
std::thread t{ [&]()
{
// run = true; !!!! BAD DESIGN !!
while (run.load())
{
std::cout << "ran!\n";
}
} };
// maybe OS preempts this thread here !!
run = false;
t.join();
在上面的代码中我们仍然可以打印
ran!
,BAD DESIGN!
有很多问题,比如
run = false
方法(使用条件变量实现)才能停止它!你无法阻止它开始!因为当您调用“stop_thread_from_starting()”时,线程可能已经启动了!wait_for_thread_to_start()
发生在线程启动之前,在启动另一个线程的同一个线程上)。
既然你已经在stopRunner
周围添加了自己的代码,你能做的最好的事情就是在调用你的代码之前检查是否设置了这个布尔值......或者也许不是,如果你检测到任务被停止,这取决于你想要发生的情况在开始之前。
runForAwhile
由于您的链接库已经使用原子布尔,因此您不需要在顶部添加任何额外的同步,原子已经建立同步。