【问题标题】:Timer with std::thread带有 std::thread 的计时器
【发布时间】:2022-10-18 21:31:31
【问题描述】:

我正在开发一个计时器类以在不同的线程上执行操作,下面的示例代码是另一个 SO 问题HERE 的副本

#include <thread>
#include <chrono>
#include <functional>

class Timer
{
public:
    ~Timer();
    Timer() noexcept;

    typedef std::chrono::milliseconds Interval;
    typedef std::function<void(void)> Timeout;

public:
    void start(const Interval& interval, const Timeout& timeout);
    void stop();

private:
    std::thread mThread;    /** Timer thread */
    bool mRunning = false;  /** Timer status */
};

带有注释的实现,其中将出现问题:

Timer::~Timer()
{
}

Timer::Timer() noexcept
{
}

void Timer::start(const Interval& interval, const Timeout& timeout)
{
    mRunning = true;

    mThread = std::thread([&]()
        {
            while (mRunning == true)
            {
                std::this_thread::sleep_for(interval);

                // std::abort will be called here
                timeout();
            }
        });
}

void Timer::stop()
{
    mRunning = false;
    mThread.join();
}

测试定时器的示例:

#include <iostream>

int main()
{
    Timer tm;

    tm.start(std::chrono::milliseconds(1000), []
        {
            std::cout << "Hello!" << std::endl;
        });

    std::this_thread::sleep_for(std::chrono::seconds(4));
    tm.stop();
}

我无法理解为什么在 lambda 中执行 std::function 时会调用 std::abort,我该如何解决这个问题?

【问题讨论】:

  • 您正在保存对临时对象的引用,该对象在您有机会调用timeout 之前被销毁。
  • 不过,这不是唯一的问题:您还需要将mRunning 设为原子。

标签: c++ multithreading timer


【解决方案1】:

start 函数的参数通过引用传递。在您的 lambda 中,您通过引用捕获它们。当您调用该 lambda 时,您捕获的所有内容都会被破坏,因此您会导致未定义的行为。

此外,请确保使用 atomic&lt;bool&gt; 而不是常规的 bool:

#include <thread>
#include <chrono>
#include <functional>
#include <cstdio>
#include <atomic>

class Timer {
public:
    ~Timer() {
        if (mRunning) {
            stop();
        }
    }
    typedef std::chrono::milliseconds Interval;
    typedef std::function<void(void)> Timeout;

    void start(const Interval &interval, const Timeout &timeout) {
        mRunning = true;

        mThread = std::thread([this, interval, timeout] {
            while (mRunning) {
                std::this_thread::sleep_for(interval);

                timeout();
            }
        });
    }
    void stop() {
        mRunning = false;
        mThread.join();
    }

private:
    std::thread mThread{};
    std::atomic_bool mRunning{};
};

int main() {
    Timer tm;

    tm.start(std::chrono::milliseconds(1000), [] {
        std::puts("Hello!");
    });

    std::this_thread::sleep_for(std::chrono::seconds(4));
}

附言你可能想根据这个想法去研究协程。

【讨论】:

  • 我尝试不使用 Timer::start(Interval interval, Timeout timeout) 引用,这不会中止,但它也不会停止计时器,欢迎使用示例修复代码。
  • @metablaster 确保通过值[=] 而不是[&amp;] 捕获它。
  • 通过值和原子捕获 lambda mRunning 使事情正常进行,非常感谢。
猜你喜欢
  • 2014-02-26
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 2018-07-11
  • 2018-06-22
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
相关资源
最近更新 更多