C++std::thread調用帶參數和返回值的函數


std::move,std::ref,引用等用法

std::bind用法

一、線程調用的函數含有參數

多線程中的函數參數如果為引用必須使用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的使用

轉載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 是三者中最底層的能力,可以用來同步不同線程之間的消息


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM