【问题标题】:Simple threaded timer, sanity check please简单的线程计时器,请进行完整性检查
【发布时间】:2015-03-05 17:00:59
【问题描述】:

我已经制作了一个非常简单的线程计时器类,并且考虑到 MT 代码存在的缺陷,我想要进行健全性检查。这里的想法是启动一个线程,然后不断循环等待一个变量。如果等待超时,则超过了间隔,我们调用回调。如果变量被发出信号,线程应该退出并且我们不调用回调。

我不确定的一件事是我的代码在析构函数中会发生什么,因为线程可能在那里(只是)可连接。我可以在析构函数中加入线程以确保它完成吗?

课程如下:

class TimerThreaded
{
public:

    TimerThreaded() {}
    ~TimerThreaded()
    {           
        if (MyThread.joinable())
            Stop();             
    }

    void Start(std::chrono::milliseconds const & interval, std::function<void(void)> const & callback)
    {   
        if (MyThread.joinable())
            Stop();

        MyThread = std::thread([=]()
        {
            for (;;)
            {
                auto locked = std::unique_lock<std::mutex>(MyMutex);
                auto result = MyTerminate.wait_for(locked, interval);

                if (result == std::cv_status::timeout)
                    callback();
                else
                    return;
            }
        });
    }

    void Stop()
    {
        MyTerminate.notify_all();
    }

private:

    std::thread MyThread;
    std::mutex MyMutex;
    std::condition_variable MyTerminate;
};

我想一个更好的问题可能是请某人将我指向一个非常简单的线程计时器,如果某处已经有一个可用的计时器。

【问题讨论】:

    标签: c++ multithreading timer


    【解决方案1】:

    我可以在析构函数中加入线程以确保它完成吗?

    不仅您可以,而且这样做很常见。如果线程实例在销毁时是可连接的(即仍在运行),则将调用terminate

    由于某种原因,结果总是超时。它似乎永远不会收到信号,因此永远不会停止。这是正确的吗? notify_all 应该解除对 wait_for 的阻塞吗?

    只有当线程恰好在 cv 上时,它才能解除阻塞。您可能正在做的是调用Start,然后在线程开始运行并开始等待之前立即调用Stop(或者可能在callback 正在运行时)。在这种情况下,线程将永远不会收到通知。

    您的代码还有另一个问题。即使您没有显式调用notify_X,在某些实现中也可能会虚假唤醒阻塞的线程。这会导致您的计时器无缘无故随机停止。

    我建议您添加一个标志变量,指示是否已调用Stop。这将解决上述两个问题。这是使用条件变量的典型方式。我什至已经为你编写了代码:

    class TimerThreaded
    {
    ...
            MyThread = std::thread([=]()
            {
                for (;;)
                {
                    auto locked = std::unique_lock<std::mutex>(MyMutex);
                    auto result = MyTerminate.wait_for(locked, interval);
    
                    if (stop_please)
                        return;
                    if (result == std::cv_status::timeout)
                        callback();
                }
            });
    ....
        void Stop()
        {
            {
                std::lock_guard<std::mutex> lock(MyMutex);
                stop_please = true;
            }
            MyTerminate.notify_all();
            MyThread.join();
        }
    
    ...
    private:
        bool stop_please = false;
    ...
    

    通过这些更改,您的计时器应该可以工作,但请注意“[std::condition_variable::wait_for] 可能会因为调度或资源争用延迟而阻塞超过 timeout_duration”,用cppreference.com 的话来说。

    如果某处已经有一个可用的线程计时器,请给我指点一个非常简单的线程计时器。

    我不知道标准的 c++ 解决方案,但现代操作系统通常提供这种功能,或者至少提供可用于构建它的部分。有关示例,请参见 Linux 上的 timerfd_create

    【讨论】:

    • 准确地说,terminate 被调用。不会抛出异常。
    • 它似乎不管用。由于某种原因,结果总是超时。它似乎永远不会收到信号,因此永远不会停止。这是正确的吗? notify_all 应该解除对 wait_for 的阻止吗?
    • @Robinson 我已经扩展了我的答案。
    • 哈哈,是的。我大约一个小时前添加了一个标志,现在它似乎可以正常工作(我选择了 std::atomic 所以不需要我假设的互斥锁)。
    猜你喜欢
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多