C 併發程式設計 thread

2022-09-15 23:33:15 字數 4179 閱讀 1201

c++11在標準庫中為多執行緒提供元件, 使用執行緒需要包含標頭檔案 thread, 其命名空間為 std.

每個程序至少有乙個執行緒: 執行main()函式的執行緒, 其餘執行緒有其各自的入口函式(執行緒函式)。

當執行緒執行完執行緒函式後, 執行緒也會退出. 如果不傳入執行緒函式(類似這種形式std::thread t;), 執行緒不會執行. 執行緒函式不能過載, 否則不能編譯.

在為乙個執行緒建立了乙個 std::thread 物件後, 如果執行緒已啟動(不傳入線程式函式時, 執行緒不會啟動), 必須要明確是加入(join)還是分離執行緒(detach).

//

啟動乙個執行緒:

void mythread(const std::string&str)

//std::thread t(mythread, "hello c...");

std::thread t( );

//對於類方法, 需要使用 std::bind.

std::thread t(std::bind(&threadexample::mythread, this, "

msg"

)); threadguard tg(t);

#ifndef _thread_guard_

#define _thread_guard_#include

class

threadguard

~threadguard()

}threadguard(

const threadguard &) = delete

; threadguard& operator=(const threadguard &) = delete

;

private

: std::thread&t;

};#endif

//_thread_guard_

如果是分離執行緒, 必須保證可訪問資料的有效性, 否則會產生未定義的行為, 如同單執行緒中乙個物件被銷毀後再訪問一樣.

處理這種情況的常規方法: 使執行緒函式的功能齊全, 將資料複製到執行緒中. 如果使用乙個可呼叫的物件作為執行緒函式,這個物件就會複製到執行緒中,而後原始物件就可以銷毀. 下面是錯誤的使用方法示例:

class

func

void

operator

() ()

std::cout

<}

};//新執行緒可能還在執行

執行緒函式可以有不同的引數, 向執行緒傳遞引數,只要在構造 std::thread 物件時,按照執行緒函式引數列表一一對應傳入即可。執行緒函式有幾點需要注意的地方:

(1) 預設的引數會被拷貝到獨立的執行緒中,即使是引用的形式, 如果需要需要傳遞引用, 需要使用 std::ref 顯示說明(並且執行緒函式引數也需要宣告為引用).

void threadparamref(std::string&str)

void threadparam(std::string

str)

std::

string str("

hello c++ thread...");

//std::thread t(threadparamref, str);

std::thread t(threadparamref, std::ref(str)); //

//std::thread t(threadparam, std::ref(str));

t.join();

std::cout

<< str << std::endl;

(2)  執行緒引數傳遞時需要注意不能傳入區域性變數, 考慮下面的**,buffer②是乙個指標變數,指向本地變數,然後本地變數通過buffer傳遞到新執行緒中②。

函式有很大的可能,會在字面值轉化成 std::string 物件之前崩潰,從而導致執行緒的一些未定義行為。

解決方案就是在傳遞到 std::thread 建構函式之前就將字面值轉化為 std::string 物件。

void f(int i,std::string

const&s);

void oops(int

some_param)

//正確的方法

void f(int i,std::string

const&s);

void not_oops(int

some_param)

(3) 執行緒函式引數傳遞時, 可以移動, 但不能拷貝. "移動"是指: 原始物件中的資料轉移給另一物件,而轉移的這些資料在原始物件中不再儲存.

void threadparamuniqueptr(std::unique_ptrup)

std::thread t(threadparamuniqueptr, std::move(up));

//std::thread t(threadparamuniqueptr, up);

//不能編譯

//std::thread t(threadparamuniqueptr, std::ref(up));

//要求執行緒函式引數也為引用才能編譯

t.join();

std::cout

<< (up.get() ? *up : -1) << std::endl; //

將輸出-1

執行緒是資源獨佔型, 但可以將所有權轉移給別的物件. 如果乙個 std::thread 物件與乙個執行的執行緒關聯, 此時接受乙個新的執行緒所有權時, 其以前關聯的執行緒將直接呼叫 std::terminate() 終止程式繼續執行.

std::thread t1(f);

std::thread t2(f);

// t1 = std::thread(f); // t1 所有權還沒有轉移, 不能通過賦乙個新值來放棄執行緒

// t1 = std::move(t2); // t1 所有權還沒有轉移, 不能通過賦乙個新值來放棄執行緒

t1.detach(); 或 t1.join();

t1 = std::move(t2);

std::thread t3 = std::move(t1);

t1 = std::move(t2);

執行緒物件也可以在函式中進行轉移.

std::thread f1()

std::thread f2()

void f3(std::thread t);

void f4()

由於 std::thread 是可轉移的, 如果容器對移動操作支援, 則可以將 std::thread 物件放入其中.

class

func

void

operator

() ()

std::cout

<}

};std::vector

threads;

for (int i = 1; i < 10; i++)

std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join)); //

對每個執行緒呼叫join()

std::thread::hardware_concurrency() 返回 cpu 核心執行緒數. 如果無法查詢系統資訊時, 返回0. (static 函式)

get_id() 返回 std::thread 物件關聯的執行緒的 id. 如果所有權已轉移, 或執行緒函式已返回, 返回0.

std::this_thread::get_id() 取得當前執行緒的 id. (static 函式)

乙個更好的threadguard

#ifndef _thread_guard_

#define _thread_guard_template

class

threadguard

~threadguard()

}threadguard(

const threadguard &) = delete

; threadguard& operator=(const threadguard &) = delete

;private

: _thread&t;

};#endif

//_thread_guard_

C 併發程式設計之thread

std thread 在 標頭檔案中宣告,因此使用 std thread 時需要包含 標頭檔案。std thread 構造 注意 可被 joinable 的 thread 物件必須在他們銷毀之前被主線程 join 或者將其設定為 detached.示例 include include include...

c 併發程式設計(二) 管理執行緒 thread類

同上節所講,執行緒是通過構造std thread物件來開始的 void do some work std thread my thread do some work 使用可呼叫型別 include includeclass func int main 如果你不做處理,在主線程結束後,thread物件...

併發程式設計 Thread和Runable 01

1.繼承thread類 不推薦 很簡單,就不說了 public class threadtest02 public static class userthread extends thread public void run 2.實現runable介面 public class runabletes...