天天看點

c++ 11實作一個定時器,周期性地執行指定的task

可以直接使用c++11的thread, mutex, conditon_variable來實作, 結合了std::function.

Note:

1)start函數,開辟一個新的線程,并detach,在這個線程中周期性地執行task。

     建立一個std::thread線程時,需要傳入一個可調用對象,這裡使用了lambda函數,非常友善。

     start函數的入參是,周期時間ms,以及一個std::function<void()>

2) 使用了c++11的原子變量automic<bool>,比較友善。

3)stop函數的作用是給已經正在運作的任務線程發一個通知(通過改變一個bool成員變量),然後使用一個conditon_variable進入等待狀态。

   任務線程會在每次執行新一輪的任務時檢查該變量的值,并判斷是否停止, 停止後,會使用condition_variable通知stop函數不用等待了。

#pragma once
#include <iostream>
#include <mutex>
#include <thread>
#include <functional>
#include <condition_variable>
#include <atomic>

using namespace std;
class MyTimer {
	
public:
	MyTimer() {
		expired = true;
		tryToExpire = false;
	}

	//interval: ms
	void start(int interval, std::function<void()> task)
	{
		
		if (expired == false)//has already started, do not start again
			return;

		// start async timer, launch thread and wait in that thread
		expired = false;
		//将lambda函數傳遞給線程,做線程函數
		std::thread([this, interval, task]() {

			//小缺點:在task執行的過程中設定tryToExpire為true的話,需要等到本次task執行完畢後才能被while條件判斷檢測到。
			while (!tryToExpire)
			{
				// sleep every interval and do the task again and again until times up
				std::this_thread::sleep_for(std::chrono::milliseconds(interval));
				task(); //call this function every interval milliseconds.
			}

			// timer be stopped, update the condition variable expired and wake main thread
			std::lock_guard<std::mutex> locker(mut);
			expired = true;
			cv.notify_one();

		}).detach();
	}

	void stop()//該接口負責修改tryToExipired的值。
	{
		// do not stop again
		if (expired)
			return;

		if (tryToExpire)
			return;

		// wait until timer 
		tryToExpire = true; // change this bool value to make timer while loop stop. This val is atomic type.

		std::unique_lock<std::mutex> locker(mut);
		cv.wait(locker, [this] {return expired == true; });//不給lambda函數串this,不能使用成員變量expired!

		// reset the timer, 
		//成功停止後,設定tryToExpire為false. 其實,可以不使用wait動作,将tryToExpire=false的動作放到
		//start函數,當while循環被打斷後,立即設定tryToExpire為false。
		//但是,這樣單獨放在這裡設定也有好處吧,實作了tryToExipired的值僅僅由該stop函數負責。例如,任務線程成功結束後,這裡可以進行一些額外的善後動作,
		
		if (expired == true)
			tryToExpire = false;

	}

private:
	condition_variable cv;
	mutex mut;//與cv配合

	atomic<bool> expired; //timer stop status
	atomic<bool> tryToExpire;//timer is in stop process.

 };


           

Test:

void printNum() {
	static int a = 0;
	a++;
	cout << "a=" << a << endl;
}
void main() {

	MyTimer obj;
	obj.start(1000,printNum);

	getchar();//定時線程執行了detatch,這裡需要等待才能看到起輸出數字到螢幕上
	obj.stop();
	getchar();
	return;
}
           

Ref:

https://blog.csdn.net/u012234115/article/details/89857431

https://www.cnblogs.com/grandyang/p/4340948.html

繼續閱讀