一、線程調用的函數含有參數
多線程中的函數參數如果為引用必須使用std::ref(函數式編程的參數默認使用拷貝方式),多線程中的函數參數如果為IO(socket應該也需要,沒有測試過)必須使用移動語義(std::move),避免多個對象同時讀寫同一個IO緩沖
點擊查看代碼
#include <thread>
#include <iostream>
void fun(int& num) //參數為int&
{
while (num < 10)
std::cout << num++;
}
void fun2(int n)
{}
int main()
{
int num = 0;
std::thread t1(fun, std::ref(num));
std::thread t2(fun, std::ref(num));
std::thread t3(fun, num); // 值傳遞函數
t1.join();
t2.join();
t3.join();
std::cout << "\nnum:" << num;
return 0;
}
二、線程調用成員函數
點擊查看代碼
#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 區別及使用
1.將函數的返回值設置為輸出參數
2.使用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();
// 如果線程執行太慢,主線程執行cout的時候result沒有計算出來會出問題
std::cout << result; //result = 5
return 0;
}
3.使用std::future、std::promise和packaged_task
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 是三者中最底層的能力,可以用來同步不同線程之間的消息
