【问题标题】:How to install a reoccurring timer function?如何安装重复计时器功能?
【发布时间】:2017-04-26 11:00:05
【问题描述】:

是否有一种简单的方法可以使用 C++/stdlib 安装定期发生的计时器功能?我想摆脱循环:

using namespace std::chrono; // literal suffixes
auto tNext = steady_clock::now();
while (<condition>) {
    std::this_thread::sleep_until(tNext);
    tNext = tNext + 100ms; 
    ...

该函数将在自己的线程中运行。

【问题讨论】:

  • 你的意思是你想让定时器异步工作,而程序做其他事情吗?然后你应该把定时进程放在它自己的线程中。
  • @JasonLang 是的,当然,在它自己的线程中
  • 你的意思是你想要类似ping(100ms, callback)的东西吗?
  • 条件变量或互斥体呢?
  • cv 或 mutex 与时间有什么关系?

标签: c++ multithreading timer thread-sleep chrono


【解决方案1】:

我猜你想要的是这个

int i = 10;
auto pred = [i]() mutable {return i--;};
auto print = []{cout << "." << endl;};

timer t{500ms};
t.push({print, pred});  //asynchronously prints '.' 10 times within 5s

//do anything else

假设性能不重要且计时器不经常更新,以下应该提供充足的功能。

#include<functional>
#include<vector>
#include<thread>
#include<utility>
#include<chrono>
#include<mutex>
#include<atomic>

class timer final
{
public:
    using microseconds = std::chrono::microseconds;

    using predicate = std::function<bool ()>;
    using callback = std::function<void ()>;
    using job = std::pair<callback, predicate>;

    explicit timer(microseconds t) : done{false}, period{t}
    {
        std::lock_guard<std::mutex> lck(mtx);

        worker = std::thread([this]{
            auto t = std::chrono::steady_clock::now();
            while(!done.load())
            {
                std::this_thread::sleep_until(t);
                std::lock_guard<std::mutex> lck(mtx);
                t += period;
                for(auto it = jobs.begin(); it != jobs.end();)
                {
                    if(it->second())
                        it++->first();
                    else
                        it = jobs.erase(it);
                }
            }
        });
    }

    ~timer()
    {
        done.store(true);
        worker.join();
    }

    void set_period(microseconds t)
    {
        std::lock_guard<std::mutex> lck(mtx);
        period = t;
    }
    void push(const callback& c)
    {
        std::lock_guard<std::mutex> lck(mtx);
        jobs.emplace_back(c, []{return true;});
    }
    void push(const job& j)
    {
        std::lock_guard<std::mutex> lck(mtx);
        jobs.push_back(j);
    }

private:
    std::mutex mtx;
    std::atomic_bool done;
    std::thread worker;

    std::vector<job> jobs;
    microseconds period;
};

timer 调用之前定期推送callbacks,当predicate 评估为false 时,从timer 中删除callbacktimer 对象有自己的生命周期,它的工作线程只有在它还活着的时候才会存在。

您希望在单个 timer 中拥有多个 jobs 的原因是,它们将被一起调用,仅使用一个线程并彼此同步。

不用担心mutex,除非您计划每秒更新计时器 >10,000 次、周期 callbacks。

【讨论】:

  • 是的,这是一个很好的解决方案,超出了我的要求,在实践中非常有用。小问题,因为我不太精通原子。你能用atomic_flag代替atomic_bool吗?我听说它是​​所有系统上唯一具有无锁保证的原子。
  • @towi 你可以让它工作,但它确实有点笨拙。但是请注意两件事:首先,无锁基本上在这里根本不重要。其次,即使标准不保证,在实践中也绝对是无锁的。
猜你喜欢
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 2023-03-28
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多