长时间运行方法的多线程抢占

问题描述 投票:0回答:1

我曾多次遇到以下情况(在 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

c++ multithreading
1个回答
0
投票
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!
有很多问题,比如

  1. 你实际上无法阻止它!因为当你这样做时你不知道它是否已经开始,所以你需要一个
    run = false
    方法(使用条件变量实现)才能停止它!
    你无法阻止它开始!因为当您调用“stop_thread_from_starting()”时,线程可能已经启动了!
  2. 幸运的是,您链接的库使用了“良好”的设计,并且会在开始工作之前检查原子布尔值,无论是否完成任何工作,这仍然是一场竞赛。 (除非
wait_for_thread_to_start()

发生在线程启动之前,在启动另一个线程的同一个线程上)。

既然你已经在

stopRunner

周围添加了自己的代码,你能做的最好的事情就是在调用你的代码之前检查是否设置了这个布尔值......或者也许不是,如果你检测到任务被停止,这取决于你想要发生的情况在开始之前。

runForAwhile

由于您的链接库已经使用原子布尔,因此您不需要在顶部添加任何额外的同步,原子已经建立同步。

© www.soinside.com 2019 - 2024. All rights reserved.