异步竞争条件或错误?

时间:2017-08-06 08:00:14

标签: c++ asynchronous std

我正在尝试使用atomic_bool来打破std :: async,代码的结构如下所示

class Test
{
   atomic_bool          IsStopped;
   std::future <void>   Future;

   Test ()
     : IsStopped (false);
   {
      LogValueWithMutex (IsStopped);

      Future = std::async (std::launch::async, &Test::AsyncFunction, this);
   }

   ~Test ()
   {
     LogValueWithMutex (IsStopped);

     IsStopped = true;

     if (Future.valid ())
       Future.get ();
   }

    void AsyncFunction ()
    {
      while (1)
      {
        // only read atomic_bool, does not write
        if (IsStopped)
          break;
      }
    }

    void CallbackFromNetworkWhichIsNotImportant ()
    {
      // Bug here!! On 2nd entry, this value is set to true! In Both Debug/Release Mode
      LogValueWithMutex (IsStopped);

      if (! IsStopped)
      {
        IsStopped = true;

        if (Future.valid ())
          Future.get ();

         // call an API which exit and will destroy this class
      }
    }
}

一切正常,异步可以正常破坏。但是,在VS2015中第2次创建类时,CallbackFromNetworkWhichIsNotImportant中的LogValueWithMutex为true。我已经验证了它的日志输出

Test ctor IsStopped: 0
Test CallbackFromNetworkWhichIsNotImportant: 0
Test dtor IsStopped: 1
Test ctor IsStopped: 0
Test CallbackFromNetworkWhichIsNotImportant: 1 <- Wrong! Who is setting this to true??!!
Test dtor IsStopped: 1

IsStopped仅在dtor和CallbackFromNetworkWhichIsNotImportant中指定为true。

所以我试着检查它是否是一个实例问题,我添加了一个新的随机变量,然后它神奇地起作用了!

class Test
{
   atomic_bool      IsStopped;
   std::future <void>   Future;
   std::string      RandomString; // HERE IS A TEST
   Test ()
     : IsStopped (false);
   {
        // NEW CODE
        ostringstream oss;
        oss << rand ()% 100;
        RandomStringToTestInstance = oss.str ();

        LogValueWithMutex (IsStopped);

        Future = std::async (std::launch::async, &Test::AsyncFunction, this);
   }
       // all parts unchanged
}

Test ctor IsStopped: 0
Test CallbackFromNetworkWhichIsNotImportant: 0
Test dtor IsStopped: 1
Test ctor IsStopped: 0
Test CallbackFromNetworkWhichIsNotImportant: 0 <- Correct!!
Test dtor IsStopped: 1

我的理解是atomic_bool不需要volatile,但不知何故它看起来像是。是否有某种优化或某些自动生成的移动操作符可能导致这种情况?

2 个答案:

答案 0 :(得分:1)

缺少代码的使用。但是我相信那里有一些多线程。多线程很棘手,很容易进入 &#39;竞争条件&#39> ,即事物顺序很重要的情况,在某些情况下可能不会预期

为了调查您的故事,我创建了一个简单的用法,可以重现您的意外输出。它不一定与您在下面创建的场景完全相同,可能还有其他类似的竞争条件。再现您的意外输出的代码是BAD(该对象在一个线程中被删除,同时仍在另一个线程中使用)但是这样的错误可能发生在多线程程序中。在代码中查找这样的错误...(除非您对 RandomString 解决方案感到满意: - )

以下是我得到的输出:

case 1:
Test::Test(): IsStopped = 0
void Test::CallbackFromNetworkWhichIsNotImportant(): IsStopped = 0
Test::~Test(): IsStopped = 1
case 2:
Test::Test(): IsStopped = 0
void Test::CallbackFromNetworkWhichIsNotImportant(): IsStopped = 1
Test::~Test(): IsStopped = 1

使用以下主要内容:

int main() {
    // OK scenario
    {
        cout << "case 1:" << endl;
        Test t;
        t.CallbackFromNetworkWhichIsNotImportant();
    }

    // Bad scenario
    std::this_thread::sleep_for(1s);
    cout << "case 2:" << endl;
    Test* tp = new Test();
    // here the error is quite verbose, but the same can be more hidden in the code
    std::thread th(&Test::CallbackFromNetworkWhichIsNotImportant, std::ref(*tp));
    delete tp;
    th.join();
}

代码: http://coliru.stacked-crooked.com/a/b8c0028766bcfc5c

另请注意

另请注意,您的CallbackFromNetworkWhichIsNotImportant功能在某些时候可以说:

// call an API which exit and will destroy this class

可能是对象在两个不同的地方被摧毁了两次。

答案 1 :(得分:0)

我发现了问题。这与async或atomic_bool无关(我第一次尝试)。我正在基于“this”分配一个回调但是只做了一次,所以当我再次启动该类时,它正在使用现有实例(因此损坏的内存)。使用atomic_bool打破循环的多线程工作正常。抱歉误报。