並行(parallel):指在同一時刻,有多條指令在多個處理器上同時執行。所以無論從微觀還是從宏觀來看,二者都是一起執行的。

C++11的標准庫中提供了多線程庫,使用時需要#include <thread>頭文件
1、線程的聲明
//聲明一個線程,名字是T,name是實現這個線程的函數名字 thread T(name); //調用一個線程 T.join(); //聲明多個線程 thread T[3]; for(int i=0;i<3;i++) { T[i]=thread(name); } //調用多個線程 for(int i=0;i<3;i++) { T[i].join() }
2、傳參
//聲明一個線程 thread T(name,int x);
3、函數join()和detach()的區別
join()和detach()函數都是用來決定線程的運行方式。當使用join方式時,會阻塞當前代碼,等待新線程完成退出后,才會繼續向下執行主線程;
而使用detach方式則不會對當前代碼造成影響,當前代碼繼續向下執行,創建的新線程同時並發執行.
舉個例子,執行下面代碼
#include<iostream> #include<thread> using namespace std; void thread2() { for (int i = 0; i < 4; i++) cout << "thread1" << endl; }//搶占式,線程1和線程2都在搶占cout這個資源 int main() { //聲明一個線程 thread T(thread2); //啟動線程的兩種方法 //T.detach(); T.join(); for (int i = 0; i < 4; i++) cout << "thread2" << endl; system("pause"); return 0; }
使用T.join()時輸出如下 使用T.detach()時輸出如下

多線程可能是並發也可能是並行,但是在使用多線程的時候,要注意資源的互斥使用,比如上面的T.detach()方式,Thread1和Thread2都在爭搶資源cout的使用,所以在輸出的時候的會有點亂,
T.join()方式也一樣,只不過上面的代碼只有Thread1在運行,沒有其它線程搶占cout的資源,所以看起來是有序的輸出
上面的cout我們叫做臨界資源,它一次只能供一個進程使用,為了實現讓輸出有序( 資源的互斥使用 ),我們要給互斥資源加鎖
4、互斥鎖的使用(mutex)
互斥鎖可以實現資源的互斥使用,頭文件是#include<mutex>
//聲明一個鎖 mutex m; //加鎖 m.lock(); /* *臨 *界 *區 */ //解鎖 m.unlock(); //自解鎖 { lock_guard<mutex> lg(m); } //使用自解鎖是為了避免因臨界區執行時出現意外,導致一直不能解鎖的情況出現 //自解鎖的作用域是在{ }內的,執行程序進入括號內會自動加鎖,出了括號外會自動解鎖
注意:要避免鎖的頻繁使用,因為它會有巨大的消耗
5、臨界資源和臨界區
臨界資源:
多道程序系統中存在許多進程,它們共享各種資源,然而有很多資源一次只能供一個進程使用。一次僅允許一個進程使用的資源稱為臨界資源。許多物理設備都屬於臨界資源,如輸入機、打印機、磁帶機等。
臨界區:
每個進程中訪問臨界資源的那段代碼稱為臨界區。顯然,若能保證諸進程互斥地進入自己的臨界區,便可實現諸進程對臨界資源的互斥訪問。為此,每個進程在進入臨界區之前,應先對欲訪問的臨界資源進行檢查,看它是否正被訪問。如果此刻該臨界資源未被訪問,進程便可進入臨界區對該資源進行訪問,並設置它正被訪問的標志;如果此刻該臨界資源正被某進程訪問,則本進程不能進入臨界區。
#include<iostream> #include<thread> #include<mutex>//互斥鎖 using namespace std; mutex m; void thread2(int index) { for (int i = 0; i < 10; i++) { m.lock(); cout << index << " thread1 " << i << endl; m.unlock(); } }//搶占式,線程1和線程2都在搶占cout這個資源 int main() { //聲明一個線程 thread T[4]; for (int i = 0; i < 4; i++) { T[i] = thread(thread2, i); } for (int i = 0; i < 4; i++) T[i].join(); for (int i = 0; i < 4; i++) cout << "thread2" << endl; system("pause"); return 0; }
6、原子操作
原子操作(atomic operation)意為“不可被中斷的一個或一系列操作”,處理器使用基於對緩存加鎖或總線加鎖的方式來實現多處理器之間的原子操作。首先處理器會自動保證基本的內存操作的原子性。處理器保證從系統內存中讀取或寫入一個字節是原子的,意思是當一個處理器讀取一個字節時,其他處理器不能訪問這個字節的內存地址。
//定義某個數據為原子操作 atomic_ +數據類型 +數據名稱 atomic_int x; atomic_double y; atomic_bool z;
#include<iostream> #include<thread> #include<mutex>//互斥鎖 #include<atomic> using namespace std; //atomic_int sum = 0; int sum = 0; void thread2(int index) { for (int i = 0; i < 1000000; i++) { sum++; } }//搶占式,線程1和線程2都在搶占cout這個資源 int main() { int n = 20; while (n--) { sum = 0; //聲明一個線程 thread T[4]; for (int i = 0; i < 4; i++) { T[i] = thread(thread2, i); } for (int i = 0; i < 4; i++) T[i].join(); cout << sum << endl; } system("pause"); return 0; }
沒有把sum定義為原子操作 把sum定義為原子操作

所以i++,是不是原子操作?(答案為否)
系統實現這個操作分三步走
1、讀內存到寄存器;
2、在寄存器中自增;
3、寫回內存
舉個例子:
有A、B兩個線程對i進行操作,初始值i=0;假設開始時間t=0;
t=1: A線程將i的內存讀到寄存器中,此時i=0
t=2:A線程在寄存器將i自增1,此時i=1
t=3:B線程將i的內存讀到寄存器,此時i=0
t=4:A線程將寄存器中的i寫回內存,i=1
t=5:B線程在寄存器將i自增1,此時i=1
t=6:B線程將寄存器中的i寫回內存,i=1
i在A、B兩個線程中各自增一次,但是最后i的值為1
如果是原子操作的話,i的值應為2
