【问题标题】:Multithreaded code - force execution order多线程代码 - 强制执行顺序
【发布时间】:2009-01-20 14:46:26
【问题描述】:

我有一些可以从两个线程访问的代码:


class Timer{
public:
   void Start(){
      start_ = clock_->GetCurrentTime();
      running_ = true;
   }

   void Read(){
      if(running_){
         time_duration remaining = clock_->GetCurrentTime() - start_;
         Actions(remaining)
      }
   }

private:
   void Actions(time_duration remaining);
   time start_;
   bool running;
};

我查看了各种库中提供的其他一些计时器,但没有找到符合我要求的计时器,因此我正在滚动自己的...

Start() 方法从一个线程调用(仅一次)。 Read() 方法从另一个线程被非常快速地调用,调用将在 Start() 被调用之前开始进入。

显然,在设置running_ 标志之前初始化start_ 变量非常重要。这可以通过添加一个互斥体来解决,该互斥体在输入 Start() 方法时被抓取......并且在在 Read() 方法中检查 running_ 之前被抓取......但这似乎有点不必要。如果这里的一切都按顺序执行,那么就没有问题。当另一个线程在 Start() 路由中时可能会发生 Read() ,例如从时钟中获取时间……Read() 发生得足够快以至于它只是不是大不了。

无论如何,我一直在寻找一种方法来确保编译器/处理器将执行

  start_ = clock_->GetCurrentTime();
  running_ = true;

指令按上面列出的顺序排列。 (或者如果我忽略了别的东西)。

【问题讨论】:

    标签: multithreading locking


    【解决方案1】:

    你需要让start_和running_volatile,然后在Start()的两个赋值之间引入内存屏障。

    【讨论】:

    • 我在考虑这些方面的一些事情......但我正在寻找更多细节。所有编译器都支持 volatile 吗?你知道任何关于内存屏障的好文档吗?
    【解决方案2】:

    为什么不去掉“running”标志,用“start”变量是否为空作为条件呢?您没有指定语言,但“开始”的某种易失性标记也是一个好主意。这也假设“开始”可以原子地编写。例如:

    class Timer{
    public:   
      void Start(){
        start_ = clock_->GetCurrentTime(); 
      }
      void Read(){
      if(nullptr != start_){
         time_duration remaining = clock_->GetCurrentTime() - start_;
         Actions(remaining)      
      }   
    }
    private:   
      void Actions(time_duration remaining);
      volatile time start_;
    };
    

    【讨论】:

    • 这不太行,因为 start_ 不是一个小的原子。
    • 我们真的到了需要指定语言才能走得更远的地步。上面可能会起作用,因为我们关心的只是 start_ 的值不再是 0,我们不必准确知道它的新值。
    • 语言是 C++。 start_ 的类型是 boost::posix_time::ptime。我不相信写一个 ptime 对象是一个原子操作,所以在上面的例子中,if check in read(更重要的是它之后的行)可以在 start_ 中只使用部分值来执行。
    • 你是对的。您总是可以在堆上创建 start_ 并存储指向它的指针。但是,在这种情况下使用单独的标志可能是更好的设计。但同样的原则也适用。 volatile 关键字应该在任何 C++ 编译器中始终可用,所以不用担心。
    【解决方案3】:

    我不确定我是否理解您的问题,但我认为您想防止在未设置 Start 方法(或未完全执行)时执行 Read ?

    如果是这种情况,您不能通过使用 AutoResetEvent 或 ManualResetEvent 来解决您的问题吗(取决于您想要的行为)。

    在 Read 方法中,您可以指定 Read 方法在 Auto/ManualResetEvent 尚未设置时等待,在 Start 方法结束时,您可以设置 Event。

    【讨论】:

      【解决方案4】:

      为什么不贴个

      if (_running == false) 开始();

      在 Read() 方法中。 并且要么使用互斥锁保护开始,要么将其定义为“关键”以确保其单线程。

      【讨论】:

      • 我不想在每次调用 Read() 时都运行...很多时候调用 Read() 时什么都不会发生。
      猜你喜欢
      • 1970-01-01
      • 2017-07-30
      • 1970-01-01
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多