線程
概念
線程在Unix系統下,通常被稱為輕量級的進程,線程雖然不是進程,但卻可以看作是Unix進程的表親,同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調用棧(call stack),自己的寄存器環境(register context),自己的線程本地存儲(thread-local storage)。 一個進程可以有很多線程,每條線程並行執行不同的任務。
優點
線程可以提高應用程序在多核環境下處理諸如文件I/O或者socket I/O等會產生堵塞的情況的表現性能。在Unix系統中,一個進程包含很多東西,包括可執行程序以及一大堆的諸如文件描述符地址空間等資源。在很多情況下,完成相關任務的不同代碼間需要交換數據。如果采用多進程的方式,那么通信就需要在用戶空間和內核空間進行頻繁的切換,開銷很大。但是如果使用多線程的方式,因為可以使用共享的全局變量,所以線程間的通信(數據交換)變得非常高效。
C++
一個簡單的Demo
#include <iostream>
#include <thread>
#include <future>
using namespace std;
void helloworld()
{
cout << "hello world \n";
}
int main()
{
//開啟一個線程
std::thread t(helloworld);
std::cout << "hello world main thread\n";
//線程的終結
t.join();
return 0;
}
多線程庫
C++11
中終於提供了多線程的標准庫,提供了線程管理、保護共享數據、線程間同步操作、原子操作等類。
多線程庫對應的頭文件是#include<thread>
,類名為std::thread
。
一個簡單的串行程序如下:
#include <iostream>
#include <thread>
void function_1() {
std::cout << "I'm function_1()" << std::endl;
}
int main() {
function_1();
return 0;
}
這是一個典型的單線程的單進程程序,任何程序都是一個進程,main()
函數就是其中的主線程,單個線程都是順序執行。
將上面的程序改造成多線程程序其實很簡單,讓function_1()
函數在另外的線程中執行:
#include <iostream>
#include <thread>
void function_1() {
std::cout << "I'm function_1()" << std::endl;
}
int main() {
std::thread t1(function_1);
// do other things
t1.join();
return 0;
}
分析:
- 首先,構建一個
std::thread
對象t1
,構造的時候傳遞了一個參數,這個參數是一個函數,這個函數就是這個線程的入口函數,函數執行完了,整個線程也就執行完了。 - 線程創建成功后,就會立即啟動,並沒有一個類似
start
的函數來顯式的啟動線程。 - 一旦線程開始運行, 就需要顯式的決定是要等待它完成(join),或者分離它讓它自行運行(detach)。注意:只需要在
std::thread
對象被銷毀之前做出這個決定。這個例子中,對象t1
是棧上變量,在main
函數執行結束后就會被銷毀,所以需要在main
函數結束之前做決定。 - 這個例子中選擇了使用
t1.join()
,主線程會一直阻塞着,直到子線程完成,join()
函數的另一個任務是回收該線程中使用的資源。
線程對象和對象內部管理的線程的生命周期並不一樣,如果線程執行的快,可能內部的線程已經結束了,但是線程對象還活着,也有可能線程對象已經被析構了,內部的線程還在運行。
線程創建
下面的程序,我們可以用它來創建一個 POSIX 線程:
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
在這里,pthread_create 創建一個新的線程,並讓它可執行。下面是關於參數的說明:
參數 | 描述 |
---|---|
thread | 指向線程標識符指針。 |
attr | 一個不透明的屬性對象,可以被用來設置線程屬性。您可以指定線程屬性對象,也可以使用默認值 NULL。 |
start_routine | 線程運行函數起始地址,一旦線程被創建就會執行。 |
arg | 運行函數的參數。它必須通過把引用作為指針強制轉換為 void 類型進行傳遞。如果沒有傳遞參數,則使用 NULL。 |
創建線程成功時,函數返回 0,若返回值不為 0 則說明創建線程失敗。
終止線程
使用下面的程序,我們可以用它來終止一個 POSIX 線程:
#include <pthread.h>
pthread_exit (status)
在這里,pthread_exit 用於顯式地退出一個線程。通常情況下,pthread_exit() 函數是在線程完成工作后無需繼續存在時被調用。
如果 main() 是在它所創建的線程之前結束,並通過 pthread_exit() 退出,那么其他線程將繼續執行。否則,它們將在 main() 結束時自動被終止。
一些函數
- std::this_thread::yield: 當前線程放棄執行,操作系統調度另一線程繼續執行。即當前線程將未使用完的“CPU時間片”讓給其他線程使用,等其他線程使用完后再與其他線程一起競爭"CPU"。
- std::this_thread::sleep_for: 表示當前線程休眠一段時間,休眠期間不與其他線程競爭CPU,根據線程需求,等待若干時間。