C++std::thread呼叫帶引數和返回值的函式
二、執行緒呼叫特殊函式
2.1 執行緒呼叫的函式含有引數
多執行緒中的函式引數如果為引用必須使用std::ref(函數語言程式設計的引數預設使用拷貝方式),多執行緒中的函式引數如果為IO(socket應該也需要,沒有測試過)必須使用移動語義(std::move),避免多個物件同時讀寫同一個IO緩衝
#include <thread> #include <iostream> void fun(int& num) //引數為int& { while (num < 10) std::cout << num++; } int main() { int num = 0; std::thread t1(fun, std::ref(num)); std::thread t2(fun, std::ref(num)); t1.join(); t2.join(); return 0; }
2.2 執行緒呼叫成員函式
#include <iostream>
#include <thread>
class A
{
public:
void display(int a) { std::cout << a << '\n'; }
};
int main()
{
A a;
std::thread t(&A::display, a, 3); //第一個引數必須帶&,第二個可帶可不帶,第二個引數之後是呼叫函式的實參
t.join();
}
三、多執行緒執行含有返回值的函式,獲取函式返回值
轉載:C++ 中 async、packaged_task、promise 區別及使用
-
將函式的返回值設定為輸出引數
-
使用std::future、std::promise和packaged_task
-
使用lambda表示式獲取函式返回值(如果執行緒執行太慢,主執行緒執行cout的時候result沒有計算出來會出問題)。
lambda表示式
#include <iostream> #include <thread> int f(int a, int b) { return a + b; } int main() { int result = 0; std::thread* t; t = new std::thread([&] { result = f(2, 3); }); t->join(); std::cout << result; //result = 5 return 0; }
std::async和std::future的使用
轉載:C++STL 執行緒:Future, Promise and async()
std::async()與std::thread()最明顯的不同就是async只是建立非同步任務,不一定建立執行緒。async()預設建立執行緒,可以設定第一個引數來決定是否建立執行緒。
- async函式原型:
std::async(std::launch::deferred,func,...) //不建立執行緒,直到呼叫get()在主執行緒執行呼叫的入口函式
std::async(std::launch::async,func,...) //會建立執行緒,且立即執行
std::async(std::launch::async | std::launch::deferred,func,...) //和std::launch::async一樣,建立執行緒
std::async(func,...) //和std::launch::async一樣,建立執行緒
- 獲取函式返回值示例程式碼:
//async的使用,配合future直接獲取多執行緒中所呼叫函式的返回值
#include <iostream>
#include <thread>
#include <future>
int f(int a, int b)
{
using namespace std::chrono_literals;
std::this_thread::sleep_for(5s); //睡眠五秒
return a + b;
}
int main()
{
std::future<int> retVal = std::async(f, 2, 4);
std::cout << "start" << '\n';
std::cout << retVal.get(); //會阻塞,即主執行緒需要子執行緒執行完從而得到返回值
std::cout << "end" << '\n';
return 0;
}
//async,future析構阻塞問題
#include <iostream>
#include <thread>
#include <future>
int main()
{
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
clock_t start = clock();
{
std::async(std::launch::async, sleep, 5); // 臨時物件被析構,阻塞 5s
std::async(std::launch::async, sleep, 5); // 臨時物件被析構,阻塞 5s
//auto f1 = std::async( std::launch::async, sleep, 5 );
//auto f2 = std::async( std::launch::async, sleep, 5 );
}
std::cout << (clock() - start) / CLOCKS_PER_SEC << std::endl;
return 0;
}
std::promise和std::future的使用
promise作為引數應該以引用的形式傳入(),因此需要使用std::ref()
future和promise的作用是在不同執行緒之間傳遞資料。使用指標也可以完成資料的傳遞,但是指標非常危險,因為互斥量不能阻止指標的訪問;而且指標的方式傳遞的資料是固定的,如果更改資料型別,那麼還需要更改有關的介面,比較麻煩;promise支援泛型的操作,更加方便程式設計處理。std::promise的作用就是提供一個不同執行緒之間的資料同步機制,它可以儲存一個某種型別的值,並將其傳遞給對應的future, 即使這個future不在同一個執行緒中也可以安全的訪問到這個值。
示例程式碼:
//promise的使用,多執行緒中的函式所使用的引數需要其他執行緒返回
//1.子執行緒使用主執行緒傳入的值
#include <thread>
#include <future>
#include <iostream>
void task(/*std::future<int> i*/std::promise<int>& i)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << i.get_future().get();
//std::cout << i.get() ; // 阻塞,直到 p.set_value() 被呼叫
}
int main()
{
//lambda表示式和函式效果一樣
//auto task = [](std::future<int> i)
//{
// std::this_thread::sleep_for(std::chrono::seconds(3));
// std::cout << i.get() << std::flush; // 阻塞,直到 p.set_value() 被呼叫
//};
std::promise<int> p;
//std::thread t(task, p.get_future());
std::thread t(task, std::ref(p));
p.set_value(5);
t.join(); //呼叫完join後執行task(),因此在這裡會阻塞
return 0;
}
//2.主執行緒使用子執行緒得到的值
//#include <iostream>
//#include <future>
//#include <thread>
//
//void task(std::promise<int>& i)
//{
// //dosomething
// int value = 8;
// i.set_value(value);
//}
//
//int main()
//{
// std::promise<int> pro;
// std::future<int> ret = pro.get_future();
//
// std::thread t(task, std::ref(pro));
// t.join();
//
// std::cout << "get value:" << ret.get() << std::endl;
//
// system("pause");
// return 0;
//}
std::packaged_task和std::future的使用
std::packaged_task的作用就是提供一個不同執行緒之間的資料同步機制,std::packaged_task本身和執行緒沒有關係,它只是關聯了一個std::future的仿函式。需要顯式的呼叫或者傳遞給std::thread進行非同步呼叫,所以它更靈活(可以選擇什麼時候開始任務)。
std::packaged_task 物件內部包含了兩個最基本元素
-
被包裝的任務(stored task),任務(task)是一個可呼叫的物件,如函式指標、成員函式指標或者函式物件
-
共享狀態(shared state),用於儲存任務的返回值,可以通過 std::future 物件來達到非同步訪問共享狀態的效果。
示例程式碼
//packaged_task的使用,直接得到多執行緒呼叫函式的返回值
#include <iostream> // std::cout
#include <utility> // std::move
#include <future> // std::packaged_task, std::future
#include <thread> // std::thread
int fun(int a)
{
std::this_thread::sleep_for(std::chrono::seconds(3));
return 2 * a;
}
int main()
{
std::packaged_task<int(int)> foo(fun);
std::future<int> ret = foo.get_future();
std::thread t(std::move(foo), 19);
t.join(); //阻塞,呼叫fun函式得到返回值
std::cout << ret.get();
return 0;
}
async、promise、paceaged_task三者的區別與聯絡
-
用 std::async 來做簡單的事情,例如非同步執行一個任務。但是要注意 std::future 析構阻塞的問題。
-
std::packaged_task 能夠很輕鬆的拿到 std::future,選擇是否配合 std::thread 進行非同步處理。同時沒有析構阻塞的問題。
-
std::promise 是三者中最底層的能力,可以用來同步不同執行緒之間的訊息