怪异的多线程行为 - compilerexplorer链接

发布于 2025-02-05 20:17:24 字数 2841 浏览 2 评论 0原文

tl; dr:为什么这 https://godbolt.org.org.org/z/z/ Z/Z/OHK31HW34 < /a>多线程程序segfault?

说明:我正在遇到多线C ++应用程序的怪异行为。该应用程序具有多个线程,这些线程会在一段时间内通过std :: Atomic&lt; bool&gt; variable循环循环。我在多个位置使用此构造,因此将其提取到具有start(function)&amp; amp;的简单threadloop类中。 stop()方法。

class ThreadLoop
{
public:
  ThreadLoop(const std::string& name) : mName(name) {}
  ~ThreadLoop() { Stop(); }

  template <typename F>
  void Start(F&& function)
  {
    if (mRunning)
      return;

    std::scoped_lock lock(mMutex);
    if (mThread.joinable())
      mThread.join();

    mRunning = true;
    mThread = std::thread([&]() {
      while (mRunning)
      {
        function();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
      }
    });
  }

  void Stop()
  {
    if (not mRunning)
      return;

    mRunning = false;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

private:
  std::atomic<bool> mRunning = false;
  std::mutex mMutex;
  std::thread mThread;
  std::string mName;
};

然后,我将此自定义类的对象用作不同的“工作”类中的成员,该类别分配了特定函数要定期执行,因此

class Worker1
{
public:
  void StartWorking()
  {
    mThread.Start([this]() { Work(); });
  }

  void StopWorking() { mThread.Stop(); }

private:
  ThreadLoop mThread{"worker1 loop"};

  void Work()
  {
    fmt::print("Working...\n");
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
};

我将所有这些“工人”在另一个类中都有所有这些“工人”,并调用startworking()/stopworking()在随机时间点上(也在threadloop中)

class Main
{
public:
  void Start()
  {
    mThread.Start([this]() { MainLoop(); });
  }

  void Stop() { mThread.Stop(); }

private:
  ThreadLoop mThread{"main loop"};
  Worker1 mWorker1;

  void MainLoop()
  {
    if (/*something*/)
      mWorker1.StartWorking();
    else
      mWorker1.StopWorking();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
};

第一个threadloop(“ main loop”)在中,MAIN类开始正常,并开始调用/stopworking(),如预期。 startWorking()然后触发工人启动自己的threadloop(“ worker1 loop”),在threadloop :: start(start()函数,例如

    `../nptl/pthread_mutex_lock.c:81: __pthread_mutex_lock: Assertion mutex->__data.__owner == 0 failed`

,根据调试器的说法,整个threadloop对象似乎是非初始化/破坏的(例如,std :: String Mname Mname变量是空的,是空的,是空的,尽管我总是提供一个非空字符串) - 这可能会导致std :: scoped_lock失败 - 锁定非初始化/破坏的静音。我的问题是,threadloop对象是如何/为什么不进行初始化的?我认为我清楚地将其构建为每个worker1对象中的成员?

TL;DR: Why does this https://godbolt.org/z/ohK31hW34 multithreaded program segfault?

Explanation: I am encountering weird behavior of my multithreaded C++ application. The application has multiple threads that loop in a while loop guarded by an std::atomic<bool> variable. I use this construct in multiple places, so I extracted it to a simple ThreadLoop class with Start(function) & Stop() methods.

class ThreadLoop
{
public:
  ThreadLoop(const std::string& name) : mName(name) {}
  ~ThreadLoop() { Stop(); }

  template <typename F>
  void Start(F&& function)
  {
    if (mRunning)
      return;

    std::scoped_lock lock(mMutex);
    if (mThread.joinable())
      mThread.join();

    mRunning = true;
    mThread = std::thread([&]() {
      while (mRunning)
      {
        function();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
      }
    });
  }

  void Stop()
  {
    if (not mRunning)
      return;

    mRunning = false;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

private:
  std::atomic<bool> mRunning = false;
  std::mutex mMutex;
  std::thread mThread;
  std::string mName;
};

I then use an object of this custom class as a member in a different 'worker' class that assigns a particular function to be periodically executed, like so

class Worker1
{
public:
  void StartWorking()
  {
    mThread.Start([this]() { Work(); });
  }

  void StopWorking() { mThread.Stop(); }

private:
  ThreadLoop mThread{"worker1 loop"};

  void Work()
  {
    fmt::print("Working...\n");
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
};

I have all of these 'workers' in another class and call StartWorking() / StopWorking() on them at random time points (also in a ThreadLoop)

class Main
{
public:
  void Start()
  {
    mThread.Start([this]() { MainLoop(); });
  }

  void Stop() { mThread.Stop(); }

private:
  ThreadLoop mThread{"main loop"};
  Worker1 mWorker1;

  void MainLoop()
  {
    if (/*something*/)
      mWorker1.StartWorking();
    else
      mWorker1.StopWorking();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
};

The first ThreadLoop ("main loop") in the Main class starts fine and starts calling StartWorking() / StopWorking(), as expected. The StartWorking() then triggers the worker to start its own ThreadLoop ("worker1 loop"), which fails undeterministically inside the ThreadLoop::Start() function, for example via

    `../nptl/pthread_mutex_lock.c:81: __pthread_mutex_lock: Assertion mutex->__data.__owner == 0 failed`

Also, according to the debugger, the entire ThreadLoop object seems to be uninitialized / destroyed (e.g. the std::string mName variable is empty, although I always provide a non-empty string) - this probably causes the std::scoped_lock to fail - locking an uninitialized/destroyed mutex. My question is, how/why is the ThreadLoop object uninitialized? I think I clearly construct it as a member inside each Worker1 object?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

人间不值得 2025-02-12 20:17:24

问题是我正在捕获t&amp;&amp;函数通过lambda中的引用threadloop :: start(),然后将线程调用lambda,这是一个悬空的参考。解决方案是捕获t&amp; amp;功能 按值

原始版本:

mThread = std::thread([&](){ ... })

固定版本:

mThread = std::thread([this, function](){ ... })

甚至更好的版本:

mThread = std::jthread([this, function](){ ... })

The issue was that I was capturing the T&& function by reference in the lambda inside ThreadLoop::Start(), and the thread then invoked the lambda, which was a dangling reference. The fix was to capture T&& function by value.

Original version:

mThread = std::thread([&](){ ... })

Fixed version:

mThread = std::thread([this, function](){ ... })

Even better version:

mThread = std::jthread([this, function](){ ... })
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文