C++多線程編程


線程

概念

線程在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;
}

分析:

  1. 首先,構建一個std::thread對象t1,構造的時候傳遞了一個參數,這個參數是一個函數,這個函數就是這個線程的入口函數,函數執行完了,整個線程也就執行完了。
  2. 線程創建成功后,就會立即啟動,並沒有一個類似start的函數來顯式的啟動線程。
  3. 一旦線程開始運行, 就需要顯式的決定是要等待它完成(join),或者分離它讓它自行運行(detach)。注意:只需要在std::thread對象被銷毀之前做出這個決定。這個例子中,對象t1是棧上變量,在main函數執行結束后就會被銷毀,所以需要在main函數結束之前做決定。
  4. 這個例子中選擇了使用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() 結束時自動被終止。

一些函數

  1. std::this_thread::yield: 當前線程放棄執行,操作系統調度另一線程繼續執行。即當前線程將未使用完的“CPU時間片”讓給其他線程使用,等其他線程使用完后再與其他線程一起競爭"CPU"。
  2. std::this_thread::sleep_for: 表示當前線程休眠一段時間,休眠期間不與其他線程競爭CPU,根據線程需求,等待若干時間。


免責聲明!

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



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