1. 程式人生 > 其它 >C++std::thread呼叫帶引數和返回值的函式

C++std::thread呼叫帶引數和返回值的函式

二、執行緒呼叫特殊函式

2.1 執行緒呼叫的函式含有引數

std::move,std::ref,引用

多執行緒中的函式引數如果為引用必須使用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的使用

轉載C++之future和promise

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 是三者中最底層的能力,可以用來同步不同執行緒之間的訊息