線程是操作系統(tǒng)進行運算調(diào)度的基本單位,進程是操作系統(tǒng)進行資源分配的基本單位。
不同進程分配不同的內(nèi)存區(qū)域,同一個進程中的不同線程共享同一片內(nèi)存。
線程的不同狀態(tài):
并行(Parallel)是真正意義上的同時運行,在同一時間點有多個程序在同時運行,比如多核CPU多個核同時運行著不同的程序。并發(fā)(ConCurrent)是在同一個時間段上多個程序同時運行,在宏觀上看是同時運行,微觀上是交替運行。
之所以設計多線程是為了充分的利用CPU資源,比如線程A正在等待磁盤數(shù)據(jù)的時候,可以將CPU讓出來,線程B就能占用CPU進行計算。
c++11新增了與操作系統(tǒng)無關的線程類thread
,使用方法如下:
void func(int i) {cout<< i<< endl;
}
int main() {thread t1(func, 10); //傳入線程綁定的函數(shù)和函數(shù)所需參數(shù)
t1.join();
return 0;
}
thread
對象創(chuàng)建之后,線程就已經(jīng)進入就緒態(tài),可以占用CPU進行運算。
線程的運行需要以具體的函數(shù)作為入口,而函數(shù)的參數(shù)傳遞無非值傳遞,引用傳遞和指針傳遞3種形式,值傳遞和指針傳遞沒什么好說的,直接復制值或者指針放入線程入口函數(shù)中,而線程的使用中需要特別注意??????引用傳遞,普通函數(shù)使用引用傳遞,底層使用的還是指針,所以函數(shù)中的引用會關聯(lián)到函數(shù)外的變量。但是在線程的使用中,函數(shù)引用傳遞是引用的復制,函數(shù)中的引用和函數(shù)外的值沒有關聯(lián)關系。如果想關聯(lián),必須傳遞參數(shù)的時候使用std::ref()
語義進行包裹。join()
和detach()
的用法:調(diào)用線程的join()
方法意味著,主線程會阻塞在該語句處,等待子線程運行結束的時候才會繼續(xù)運行主線程,而detach()
方法意味著,子線程從主線程中分離出來各自運行,子線程變?yōu)楹笈_守護線程(deamon thread),如此這般,子線程可能會結束在主線程之后,可能會出現(xiàn)不可預知的問題。
當存在多個線程共同訪問同一資源(比如隊列)的時候,如果操作的順序不當可能會出現(xiàn)不可預知的錯誤,該資源成為臨界資源,此時C++使用互斥量mutex
進行資源訪問的控制。
通常為了方便理解多線程操作,我們將mutex
稱為鎖🔒,對資源的訪問稱為對資源上鎖和解鎖。實際上mutex
是一個標記量,不同的線程對同一mutex
對象進行lock()
和unlock()
可以達到控制資源訪問次序的目的。lock()
方法是嘗試獲取鎖并加鎖,如果獲取不到鎖,線程就會阻塞在此語句處,mutex
調(diào)用了lock()
就必須unlock()
,否則就有可能是其他線程不能成功lock()
而一直阻塞。
為了解決這種可能忘記unlock()
的問題,引入lock_guard
模版類,lock_guard
類似于智能指針,出作用域會自動unlock()
。和lock_guard
相似的一個模版類是unique_lock
,使用方法和lock_guard
類似。
多個線程需要通信的時候會用到cv
,比如消費者和生產(chǎn)者線程模型,這兩個線程并不是單單的對資源解鎖和加鎖。而是需要通信,比如,消費者沒有資源可以消費的時候需要將生產(chǎn)者叫醒。cv
有兩個方法wait()
和wait_for()
,wait
方法會讓當前線程釋放鎖然后阻塞,直到被喚醒;wait_for
方法需要傳遞一個時間參數(shù),當前線程釋放鎖然后等待被喚醒,但是不會一直等待,而是吵過時間參數(shù)自動蘇醒。cv
還有兩個方法notify_one()
和notify_all()
,notify_one
會喚醒線程等待隊列的第一個線程,而notify_all
會喚醒所有等待的線程。wait
和sleep
方法的區(qū)別:
wait
是cv
的方法,sleep
是this_thread
的方法;wait
會釋放鎖然后再阻塞,而sleep
直接阻塞,不會釋放鎖。前面提到的線程入口函數(shù)都是實現(xiàn)一系列的操作但是沒有返回值,如果需要線程的返回值,我們需要使用std::future<>
這樣一個模版類來接受返回值,而普通的用thread
創(chuàng)建線程的方式不能獲得future
對象,此時有數(shù)種方法:
std::async()
,顧名思義,異步任務就是啟動一個和主線程并發(fā)的任務,該方法會返回一個future對象,通過該對象的get()方法就能獲得線程返回的對象,使用方法如下:int func() {return 1234;
}
int main() {std::futurefval = std::async(func);
std::cout<< fval.get()<< std::endl;
return 0;
}
主線程會阻塞在get()處,直到線程返回返回值。async()
方法還可以傳入一個枚舉標志量std::launch
,如果將此標記量設置為deferred
,意味著方法調(diào)用處不會立即創(chuàng)建線程,直到調(diào)用get()
方法才會創(chuàng)建方法并運行,而此時和直接在主線程調(diào)用線程入口函數(shù)是一樣的,是串行運行。注意??:get()
方法只能調(diào)用一次;
2. 使用std::package_task<>
包裹一個函數(shù),然后傳遞給一個線程對象,線程運行起來之后用package_task
對象的get_future()
方法獲得future
對象,然后再用get()
方法拿到返回值。package_task
使用方法如下:
int func() {return 1234;
}
int main() {std::packaged_tasktask(func);
std::thread t1(std::ref(task));
t1.join();
std::cout<< task.get_future().get()<< std::endl;
return 0;
}
std::promise<>
模版類可以從線程中將值帶到線程外,promise
對象會傳遞到線程入口函數(shù)中去,然后在線程運行過程中,使用set_value()
方法給promise
對象賦值,然后在異步線程之外,使用promise
對象的get_future()
方法獲得future
對象,之后的操作和以上就一樣了。使用方法如下:void func(std::promise&pro) {pro.set_value(100);
}
int main() {std::promisepro;
std::thread t1(func, std::ref(pro));
std::cout<< pro.get_future().get()<< std::endl;
t1.join(); //此處使用join其實意義不大,因為get已經(jīng)獲取到值說明異步線程已經(jīng)結束
return 0;
}
六、線程的其他使用wait
被喚醒之后第一件事是會反復嘗試直到拿到鎖再往下執(zhí)行,wait
被喚醒拿到鎖之后一般還要再次檢查阻塞條件保證多線程共享的正確性;unique_lock
所有權的轉(zhuǎn)移類似于unique_ptr
,使用move
進行所有權轉(zhuǎn)移;std::lock(mutex1, mutex2)
方法可以嘗試同時鎖住多個互斥量,如果有一個沒鎖成功,就不會對任何互斥量進行加鎖;release()
方法將unique_lock
和mutex
解綁,并返回之前綁定的mutex
;mutex
,lock()
和unlock()
之間包裹的語句多少稱為鎖的粒度,粒度要合適,如果太小可能不能達到上鎖的預期,如果太大,會降低多線程運行效率;std::adopt_lock
可以傳遞給lock_guard
和unique_lock
的構造函數(shù),表示這個互斥量已經(jīng)被lock了(你必須要把互斥量提前l(fā)ock了 ,否者會報異常);std::adopt_lock
標記的效果就是假設調(diào)用一方已經(jīng)擁有了互斥量的所有權(已經(jīng)lock成功了);通知lock_guard
不需要再構造函數(shù)中l(wèi)ock這個互斥量了。unique_lock
也可以帶std::adopt_lock
標記,含義相同,就是不希望再unique_lock
的構造函數(shù)中l(wèi)ock這個mutex
。用std::adopt_lock
的前提是,自己需要先把mutex
lock上;用法與lock_guard
相同;std::try_to_lock
會嘗試用mutex
的lock去鎖定這個mutex
,但如果沒有鎖定成功,也會立即返回,并不會阻塞在那里,用這個try_to_lock
的前提是你自己不能先lock;std::defer_lock
的前提是,你不能自己先lock,否則會報異常,std::defer_lock
的意思就是并沒有給mutex
加鎖,初始化了一個沒有加鎖的mutex
。你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧