C++並發練習 筆記(一)STL<future>中的packaged_task
2016-11-15 18:45
309 查看
今天在偶然在找尋有關OpenCV的演算法實作方法時,看到了大牛們使用了packaged_task的這樣一個函數。
自己是沒見過的,當然就趕快操起google搜尋及手邊資料進行搜查。
並自己在網絡上找了一些視頻資源加以學習,並在此留下筆記,用來強化記憶及供人及自己日後參考。
Bo qian大神的一系列視頻講的真是不錯,每次都能從他的代碼中學到許多東西,因此真心推薦。
進入正文:
首先先附上來自cppreference.com的C++字典說明。來源連結
packaged_task被定義在頭文件<future>當中,而看到<future>這個頭文件,應該很多同學都跟我一樣心裡有了個底,這是個將會是個異步函數的調用
,就跟我們想的一樣,它確實就是如此。
這個模板函數能封裝任何callable的物件,包括function,lambda,bind expresion,function object,)而被包裝在其中的函數
將會被以異步的方式進行調用。
這是它的建構子。
恩..確實有些難懂,不如先來看一下大至的使用方法
在main函數中 std::packaged_task<>模板函數所包含的的是int();
其中int 代表的是被包裝函數的返回值,()不加入任何類型代表不帶任何參數,假如有參數則可以寫成如下
而在本範例中,要被包裝的函數是 int Factorial (int),理應當寫成
但是卻寫成了
仔細看一下後面,原來被包裝的函數與參數綁定了,bind(Factorial,6),使得原本的模版函數不能在接受更多參數
故而最後寫成
這是在應用上須要相當注意的地方。
t現在代表了一個異步調用,如果在之後想要獲取它的值,可以直接寫以下代碼來獲取它的值
但是你看到接下來的代碼
我們將它放入了一個雙向對列之中,這是為了接下來比較有挑戰性的法,同時使用thread和異步調用。
來看一下剛剛範例的進階版,斜體加粗的部分代表跟剛剛比較多出來的部分。
這樣代碼就成了異步的生產與消費者線程模型。
但這樣的代碼仍然沒有考慮到資源競爭,以及線程同步的問題,讓我們繼續完善它。(使用Mutex 跟lock_guard)
但這樣仍無法保證,生產者將工作塞入容器前,消費者不能先將物品取出來,必須確認容器不為空時才繼續執行動作,因此我們再導入
condition_variable
自己是沒見過的,當然就趕快操起google搜尋及手邊資料進行搜查。
並自己在網絡上找了一些視頻資源加以學習,並在此留下筆記,用來強化記憶及供人及自己日後參考。
推薦學習視頻連結:youtube:C++ Threading #9: packaged_task
Bo qian大神的一系列視頻講的真是不錯,每次都能從他的代碼中學到許多東西,因此真心推薦。
進入正文:
首先先附上來自cppreference.com的C++字典說明。來源連結
packaged_task被定義在頭文件<future>當中,而看到<future>這個頭文件,應該很多同學都跟我一樣心裡有了個底,這是個將會是個異步函數的調用
,就跟我們想的一樣,它確實就是如此。
這個模板函數能封裝任何callable的物件,包括function,lambda,bind expresion,function object,)而被包裝在其中的函數
將會被以異步的方式進行調用。
這是它的建構子。
template< class > class packaged_task; //not defined | (1) | (since C++11) |
template< class R, class ...Args > class packaged_task<R(Args...)>; | (2) | (since C++11) |
int Factorial(int N) //被包裝函數的宣告及實現 { int res=1; for(int i=N;i>1;i--) res*=i; std::cout<<"Result is"<<res<<std::endl; return res; } std::deque<std::packaged_task<int()>> task_q< 4000 span style="color:#406080;">; //packeged_task的容器 int main() { std::packaged_task<int()> t(bind(Fatorial,6)); //包裝函數 task_q.push_back(t); //將被包裝的函數丟入容器中 return 0; }
在main函數中 std::packaged_task<>模板函數所包含的的是int();
其中int 代表的是被包裝函數的返回值,()不加入任何類型代表不帶任何參數,假如有參數則可以寫成如下
std::packaged_task<int (int ,string&)>//接受兩個參數 int,string&,並返回int
而在本範例中,要被包裝的函數是 int Factorial (int),理應當寫成
std::packaged_task<int (int)>//接受一個參數 int,並返回int
但是卻寫成了
std::packaged_task<int ()>//不接受參數,並返回int
仔細看一下後面,原來被包裝的函數與參數綁定了,bind(Factorial,6),使得原本的模版函數不能在接受更多參數
故而最後寫成
std::packaged_task<int()> t(bind(Factorial,6))//不接受參數,並返回int,同時包裝了bind function obj (Factorial,6)
這是在應用上須要相當注意的地方。
t現在代表了一個異步調用,如果在之後想要獲取它的值,可以直接寫以下代碼來獲取它的值
std::future<int> nAsyncResult=t.get_future()
但是你看到接下來的代碼
task_q.push_back(t);
我們將它放入了一個雙向對列之中,這是為了接下來比較有挑戰性的法,同時使用thread和異步調用。
來看一下剛剛範例的進階版,斜體加粗的部分代表跟剛剛比較多出來的部分。
int Factorial(int N) { int res=1; for(int i=N;i>1;i--) res*=i; cout<<"Result is"<<res<<endl; return res; } std::deque<std::packaged_task<int()>> task_q; void thread_1()//線程函數的實作 { std::packaged_task<int()>t;//宣到一個封裝int()函數類型的package_task t=std::move(task_q.front());//t=容器最上方的值,由於容器中的右值不再使用,所以利用move語句,將右值直接給t,而不是拷貝一份。 t(); //執行t} int main() { std::thread t1(thread_1); std::packaged_task<int()> t(bind(Fatorial,6)); task_q.push_back(t); t1.join();//確保主線程等待執行緒結束 return 0; }
這樣代碼就成了異步的生產與消費者線程模型。
但這樣的代碼仍然沒有考慮到資源競爭,以及線程同步的問題,讓我們繼續完善它。(使用Mutex 跟lock_guard)
int Factorial(int N) { int res=1; for(int i=N;i>1;i--) res*=i; cout<<"Result is"<<res<<endl; return res; } std::deque<std::packaged_task<int()>> task_q; std::mutex mu //宣告臨界區mutex void thread_1() { std::packaged_task<int()>t; { std::lock_guard<std::mutex> locker(mu); //lock guard必須與生產者(主線程使用同一個臨界區 t=std::move(task_q.front()); task_q.pop_front(); } t(); } int main() { std::thread t1(thread_1); std::packaged_task<int()> t(bind(Fatorial,6)); std::future<int> nFuture=t.get_future(); //在將來以異步的方式獲取該值 { std::lock_guard<std::mutex> locker(mu); //lock guard必須與消費者(線程1)使用同一個臨界區 task_q.push_back(std::move(t)); } t1.join(); return 0; }
但這樣仍無法保證,生產者將工作塞入容器前,消費者不能先將物品取出來,必須確認容器不為空時才繼續執行動作,因此我們再導入
condition_variable
int Factorial(int N) { int res=1; for(int i=N;i>1;i--) res*=i; cout<<"Result is"<<res<<endl; return res; } std::deque<std::packaged_task<int()>> task_q; std::mutex mu std::condition_variable cond; //宣告condition_variable void thread_1() { std::packaged_task<int()>t; { std::unique_lock<std::mutex> locker(mu); //使用condition_variable,lock的方法必須為unique_lock cond.wait(locker,[](){return !task_q.empty();}); //使用lambda function來指示當容器為空,則不執行任何動作。 t=std::move(task_q.front()); task_q.pop_front(); } t(); } int main() { std::thread t1(thread_1); std::packaged_task<int()> t(bind(Fatorial,6)); std::future<int> nFuture=t.get_future(); { std::lock_guard<std::mutex> locker(mu); task_q.push_back(std::move(t)); } cond.notify_one(); //通知另一個在等待的線程可以繼續執行,如果多線程使用notify_all() cout<<fu.get(); //獲取異步調用的值,並打印 t1.join(); return 0; }
相关文章推荐
- C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)
- C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)
- C++11并发编程指南四(<future> 详解二 std::packaged_task 介绍)
- C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)
- C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)
- C++ STL <vector>
- 【C++ STL】算法 <algorithm>中各种算法解析 分类: C/C++ 数据结构与算法 2015-07-07 21:37 22人阅读 评论(0) 收藏
- C++11多线程(六):《<future> 详解二:std::packaged_task 介绍》
- 【C++ STL】算法 <algorithm>中各种算法解析
- 【C++ STL】算法 <algorithm>中各种算法解析
- [翻译] C++ STL容器参考手册(第二章 <deque>)
- 【C++ STL】算法 <algorithm>中各种算法解析
- 【C++ STL】算法 <algorithm>中各种算法解析
- 浅谈STL list<T>链表容器和迭代器的使用C++实现
- c++ stl map<> 容器测试
- C++ STL <map> 用法简介
- 【C++ STL】算法 <algorithm>中各种算法解析
- 【C++ STL】算法 <algorithm>中各种算法解析
- c++ STL ostream_iterator<> demo
- 【C++ STL】算法 <algorithm>中各种算法解析