C++造轮子飙车现场之开发一个定时器框架,支持周期性执行任务,取消任务

先看用法:

添加了4个任务,全都首次不执行,
任务1周期性执行,间隔500ms
任务2只执行一次
任务3只执行一次
任务4周期性执行,间隔3000ms。

void TestTask1() {
    
    
    std::cout << "Task 1 executed" << std::endl;
}

void TestTask2() {
    
    
    std::this_thread::sleep_for(100ms);
    std::cout << "Task 2 executed" << std::endl;
}

void TestTask3() {
    
    
    std::this_thread::sleep_for(200ms);
    std::cout << "Task 3 executed" << std::endl;
}

void TestTask4() {
    
    
    std::this_thread::sleep_for(300ms);
    std::cout << "Task 4 executed" << std::endl;
}

int main() {
    
    
    TaskManager task_mgr;
    task_mgr.Start();
    
    auto task_id1 = task_mgr.AddTask(Task(500ms, TestTask1, false));
    auto task_id2 = task_mgr.AddTask(Task(1000ms, TestTask2, false, false));
    auto task_id3 = task_mgr.AddTask(Task(2000ms, TestTask3, false, false));
    auto task_id4 = task_mgr.AddTask(Task(3000ms, TestTask4, false));
	
    std::this_thread::sleep_for(5000ms);

    task_mgr.RemoveTask(task_id2);
    task_mgr.RemoveTask(task_id3);

    std::this_thread::sleep_for(8000ms);

    task_mgr.Stop();

    return 0;
}

以下是实现:

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <map>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>

using namespace std::chrono_literals;

class Task {
    
    
 public:
  Task(std::chrono::milliseconds interval, std::function<void()> func,
       bool run_for_first_time = true, bool repeat = true)
      : id_(boost::uuids::random_generator()()),
        interval_(interval),
        func_(std::move(func)),
        repeat_(repeat) {
    
    
    next_run_time_ = run_for_first_time
                         ? std::chrono::steady_clock::now()
                         : std::chrono::steady_clock::now() + interval_;
  }

  const boost::uuids::uuid& GetId() const {
    
     return id_; }
  void Run() {
    
    
    func_();
    if (repeat_) {
    
    
      // 计算出下一次执行时间点
      next_run_time_ += interval_;
      
      // 由于func本身的执行会消耗时间,我们需要判断是否超出了一个执行周期
      auto now = std::chrono::steady_clock::now();
      auto diff = now - next_run_time_;
      if (diff > std::chrono::milliseconds::zero()) {
    
    
        // 如果差值已经超过了一个周期,则直接将下一次执行时间推迟一个周期
        next_run_time_ += std::chrono::duration_cast<std::chrono::milliseconds>(
            std::ceil(diff.count() / static_cast<double>(interval_.count())) *
            interval_);
      }
    }
  }
  bool Repeat() const {
    
     return repeat_; }
  std::chrono::steady_clock::time_point GetNextRunTime() const {
    
    
    return next_run_time_;
  }
  // 提供给优先级队列做比较使用, 优先级队列会把大的数放到前面,
  // next_run_time_的值较大的, 说明不需要立刻执行, 应看成比较小。
  bool operator<(const Task& other) const {
    
    
    return next_run_time_ > other.next_run_time_;
  }

 private:
  boost::uuids::uuid id_;
  std::chrono::milliseconds interval_;
  std::function<void()> func_;
  bool repeat_;
  std::chrono::steady_clock::time_point next_run_time_;
};

class TaskManager {
    
    
 public:
    explicit TaskManager(int thread_count = 1) : exit_(false) {
    
    
    SetThreadCount(thread_count);
    
    task_queue_.resize(thread_count_);
    for (int i = 0; i < thread_count_; ++i) {
    
    
      queue_mutex_.push_back(std::make_unique<std::mutex>());
    }
    
    for (int i = 0; i < thread_count_; ++i) {
    
    
      cv_.push_back(std::make_unique<std::condition_variable>());
    }
  }

  boost::uuids::uuid AddTask(Task task) {
    
    
    {
    
    
      std::lock_guard<std::mutex> lock(mutex_);
      tasks_.insert(std::make_pair(task.GetId(), task));
    }

    {
    
    
      auto index = *task.GetId().begin() % thread_count_;
      std::lock_guard<std::mutex> lock(*queue_mutex_[index]);
      task_queue_[index].push(task);
      cv_[index]->notify_all();
    }

    return task.GetId();
  }

  void RemoveTask(const boost::uuids::uuid& task_id) {
    
    
    {
    
    
      std::lock_guard<std::mutex> lock(mutex_);
      tasks_.erase(task_id);
    }
  }

  bool Contains(const boost::uuids::uuid& task_id) {
    
    
    {
    
    
      std::lock_guard<std::mutex> lock(mutex_);
      return tasks_.count(task_id) > 0;
    }
  }

  void Start() {
    
    
    for (int i = 0; i < thread_count_; ++i) {
    
    
      threads_.emplace_back([&]() {
    
    
        while (!exit_) {
    
    
          std::unique_lock<std::mutex> lock(*queue_mutex_[i]);
          while (!task_queue_[i].empty() &&
                 task_queue_[i].top().GetNextRunTime() <=
                     std::chrono::steady_clock::now()) {
    
    
            auto task = task_queue_[i].top();
            task_queue_[i].pop();

            if (Contains(task.GetId())) {
    
     // 判断是否还在tasks_中,因为任务可能被人取消了,取消就不执行了
              task.Run();
              if (task.Repeat()) {
    
     // 需要重复执行则归还到优先级队列
                task_queue_[i].push(task);
              } else {
    
    
                RemoveTask(task.GetId()); // 不需要重复执行则从tasks_中删除,不需要从queue中删除,因为下次取出自然会删除
              }
            }
          }
          if (!exit_) {
    
    
            auto wait_time = task_queue_[i].empty()
                                 ? 10ms
                                 : (task_queue_[i].top().GetNextRunTime() -
                                    std::chrono::steady_clock::now());
            // 等待的时间是根据下一个需要执行的任务精确计算出来的,如果没有任务要执行,那就睡10ms
            cv_[i]->wait_for(lock, wait_time);
          }
        }
      });
    }
  }

  void Stop() {
    
    
    exit_ = true;
    for (int i = 0; i < thread_count_; ++i) {
    
    
      cv_[i]->notify_all();
    }

    for (auto& thread : threads_) {
    
    
      thread.join();
    }
  }

 private:
   void SetThreadCount(int count) {
    
    
    thread_count_ = std::min(static_cast<unsigned int>(std::max(count, 1)),
                             std::thread::hardware_concurrency());
  }

 private:
  std::map<boost::uuids::uuid, Task> tasks_;
  std::vector<std::priority_queue<Task>> task_queue_;
  std::vector<std::unique_ptr<std::mutex>> queue_mutex_;
  std::mutex mutex_;
  std::vector<std::unique_ptr<std::condition_variable>> cv_;
  bool exit_;
  int thread_count_{
    
    1};
  std::vector<std::thread> threads_;
};

void TestTask1() {
    
     std::cout << "Task 1 executed" << std::endl; }

void TestTask2() {
    
    
  // std::this_thread::sleep_for(std::chrono::milliseconds(100));
  std::cout << "Task 2 executed" << std::endl;
}

void TestTask3() {
    
    
  // std::this_thread::sleep_for(std::chrono::milliseconds(200));
  std::cout << "Task 3 executed" << std::endl;
}

void TestTask4() {
    
    
  // std::this_thread::sleep_for(std::chrono::milliseconds(300));
  std::cout << "Task 4 executed" << std::endl;
}

int main() {
    
    
  TaskManager task_mgr;
  task_mgr.Start();

  auto task_id1 = task_mgr.AddTask(Task(500ms, TestTask1, false));
  auto task_id2 = task_mgr.AddTask(Task(1000ms, TestTask2, false, false));
  auto task_id3 = task_mgr.AddTask(Task(2000ms, TestTask3, false, false));
  auto task_id4 = task_mgr.AddTask(Task(3000ms, TestTask4, false));

  std::this_thread::sleep_for(5000ms);

  task_mgr.RemoveTask(task_id2);
  task_mgr.RemoveTask(task_id3);

  std::this_thread::sleep_for(8000ms);

  task_mgr.Stop();

  return 0;
}

待优化空间:

猜你喜欢

转载自blog.csdn.net/HandsomeHong/article/details/129675437
今日推荐