線程的概念: 每個正在系統上運行的程式都是一個進程。每個進程包含一到多個線程。進程也可能是整個程式或者是部分程式的動態執行。線程是一組指令的集合,或者是程式的特殊段,它可以在程式里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程式里執行多任務。通常由操作系統負 ...
線程的概念:
- 每個正在系統上運行的程式都是一個進程。每個進程包含一到多個線程。進程也可能是整個程式或者是部分程式的動態執行。線程是一組指令的集合,或者是程式的特殊段,它可以在程式里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程式里執行多任務。通常由操作系統負責多個線程的調度和執行。
- 線程是程式中一個單一的順序控制流程.在單個程式中同時運行多個線程完成不同的工作,稱為多線程.
- 線程和進程的區別在於,子進程和父進程有不同的代碼和數據空間,而多個線程則共用數據空間,每個線程有自己的執行堆棧和程式計數器為其執行上下文.多線程主要是為了節約CPU時間,發揮利用,根據具體情況而定. 線程的運行中需要使用電腦的記憶體資源和CPU。
多線程的概念
- 多線程是指從軟體或者硬體上實現多個線程併發執行的技術.
- 多線程是為了同步完成多項任務,不是為了提高運行效率,而是為了提高資源使用效率來提高系統的效率。線程是在同一時間需要完成多項任務的時候實現的。
- 最簡單的比喻多線程就像火車的每一節車廂,而進程則是火車。車廂離開火車是無法跑動的,同理火車也不可能只有一節車廂。多線程的出現就是為了提高效率。
如果你的應用程式需要採取以下的操作,那麼你盡可在編程的時候考慮多線程機制:
- 連續的操作,需要花費忍無可忍的過長時間才可能完成
- 並行計算
- 為了等待網路、文件系統、用戶或其他I/O響應而耗費大量的執行時間
- 所以說,在動手之前,先保證自己的應用程式中是否出現了以上3種情形。
為什麼需要多線程(解釋何時考慮使用線程)
- 從用戶的角度考慮,就是為了得到更好的系統服務;從程式自身的角度考慮,就是使目標任務能夠儘可能快的完成,更有效的利用系統資源。綜合考慮,一般以下場合需要使用多線程:
- 程式包含複雜的計算任務時,主要是利用多線程獲取更多的CPU時間(資源)。
- 處理速度較慢的外圍設備.比如:列印時。再比如網路程式,涉及數據包的收發,時間因素不定。使用獨立的線程處理這些任務,可使程式無需專門等待結果。
- 程式設計自身的需要.WINDOWS系統是基於消息迴圈的搶占式多任務系統,為使消息迴圈系統不至於阻塞,程式需要多個線程的來共同完成某些任務。
- 每個正在系統上運行的程式都是一個進程。每個進程包含一到多個線程。進程也可能是整個程式或者是部分程式的動態執行。線程是一組指令的集合,或者是程式的特殊段,它可以在程式里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程式里執行多任務。通常由操作系統負責多個線程的調度和執行
線程的優先順序
- 優先順序的取值為1-10(數值越高優先順序越高)。
- Public final int getPriority(); 得到線程優先順序的數值。
- Public final void setPriority(int newPriority);修改線程的優先順序。
- 註:優先順序高不代表該線程就一定先運行,只能代表該線程先運行的可能型比較大。
控制線程周期常用的方法
- Wait()釋放CPU的執行權,釋放鎖。
- Notify()回到wait前的狀態。
- Yied()讓線程臨時暫停。(讓線程將資源釋放出來)
- Join()讓該線程強行加入執行。
- SetDaemon(true)設置該線程為後臺線程(當前臺線程結束時,後臺線程一定會一起結束)。
- 註:結束線程原理就是讓run方法結束,所以只要控制run的流程即可。
為什麼要線程同步
- 線程間共用代碼和數據可以節省系統開銷,提高效率。但也同時會導致“數據訪問衝突”。如何實現線程間有機交互,並確保共用資源在某時只能被一個線程訪問,就是線程同步。
- 多個線程間共用的數據稱為臨界資源。
多線程的同步與互斥:
方式一:鎖
- 在主線程中初始化鎖為解鎖狀態
- pthread_mutex_t mutex;
- pthread_mutex_init(&mutex, NULL);
- 在編譯時初始化鎖為解鎖狀態
- 鎖初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- 訪問對象時的加鎖操作與解鎖操作
- 加鎖 pthread_mutex_lock(&mutex)
- 釋放鎖 pthread_mutex_unlock(&mutex)
互斥鎖
- 每個對象都對應一個互斥鎖標記,可以保證在某一時刻只能有一個線程訪問該對象。
- 互斥鎖的關鍵字 synchronized 可以寫在某個方法上(代表鎖調用該方法的對象); 可以括在要鎖的語句外。
- 好處:解決了線程安全的問題
- 弊端:降低了運行效率(判斷鎖,且不能共用信息);容易出現死鎖。
死鎖:
- 兩個線程A,B用到同一個對象s(s為共用資源),且線程A在執行中要用到B運行後所創造條件。在這種前提下A先開始運行,進入同步塊後,對象s被鎖定,接著線程A因等待B運行結束而進入阻塞狀態,於是B開始運行,但因無法訪問對象s,線程B也進入阻塞狀態,等待s被線程A解鎖。最終的結果:兩個線程互相等待,都無法運行。
方式二:信號量
鎖有一個很明顯的缺點,那就是它只有兩種狀態
:鎖定與不鎖定。
信號量本質上是一個非負數的整數計數器,它也被用來控制對公共資源的訪問。當公共資源增加的時候,調用信號量增加函數sem_post()對其進行增加,當公共資源減少的時候,調用函數sem_wait()來減少信號量。其實,我們是可以把鎖當作一個0-1信號量的。
它們是在/usr/include/semaphore.h
中進行定義的,信號量的數據結構為sem_t, 本質上,它是一個long型整數
相關函數
在使用semaphore之前,我們需要先引入頭文件#include <semaphore.h>
- 初始化信號量:
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 成功返回0,失敗返回-1
- 參數
- sem:指向信號量結構的一個指針
- pshared: 不是0的時候,該信號量在進程間共用,否則只能為當前進程的所有線程們共用
- value:信號量的初始值
- 信號量減1操作,當sem=0的時候該函數會堵塞
int sem_wait(sem_t *sem);
- 成功返回0,失敗返回-1
- 參數
- sem:指向信號量的一個指針
- 信號量加1操作
int sem_post(sem_t *sem);
- 參數與返回同上
- 銷毀信號量
int sem_destroy(sem_t *sem);
- 參數與返回同上