20191320-2021-2022-1-diocs 學習筆記5
第4章 併發程式設計
4.1~4.2並行概念
並行:平行計算是一種計算方案,它嘗試使用多個執行並行演算法的處理器更快速地解決問題。
順序演算法和並行演算法、併發
順序演算法和並行演算法的區別:
順序演算法按照所有的步驟進行單步執行。
並行演算法按照指定的並行演算法執行獨立的任務。
理性情況下,並行演算法中的所有任務都應該同時實時執行,然而在真正的並行執行只能在多核或多處理器系統中實現,在單CPU系統中,只能併發執行,即在邏輯上並行執行。
執行緒
執行緒的定義在程序之下,程序是獨立的執行單元,在核心模式下,進行在唯一的地址空間上執行。而執行緒則是某程序統一地址空間上的獨立執行單元。主執行緒可以建立其他執行緒,每個執行緒又可以建立更多的執行緒。
某程序的所有執行緒都在該程序的相同地址空間中執行,但每個執行緒都是一個獨立的執行單元。
相比程序,執行緒好像相對更為輕量級。且執行緒還具有以下優點:
- 執行緒建立和切換速度更快:若要在某個程序中建立執行緒,作業系統不必為新的執行緒分配記憶體和建立頁表,因為執行緒與程序共用同一個地址空間。所以,建立執行緒比建立程序更快。
- 執行緒的響應速度更快:一個程序只有一個執行路徑。當某個程序被掛起時,幫個程序都將停止執行。相反,當某個執行緒被掛起時,同一程序中的其他執行緒可以繼續執行。
- 執行緒更適合井行計算:平行計算的目標是使用多個執行路徑更快地解決間題。基於分治原則(如二叉樹查詢和快速排序等)的演算法經常表現出高度的並行性,可通過使用並行或併發執行來提高計算速度。
但是,執行緒也存在問題,需要明確的同步資訊,也存在安全性問題。如果在但CPU上,處理多執行緒程式需要各個執行緒來回切換,反而增大了開銷,降低了速度。
4.3~4.5 執行緒操作和程式例項
Linux下Pthread庫提供的執行緒API:
pthread_create(thread, attr, function, arg): create thread
pthread_exit(status):terminate thread
pthread_cancel(thread) : cancel thread
pthread_attr_init(attr) : initialize thread attributes
pthread_attr_destroy(attr): destroy thread attribute
建立執行緒
pthread_create()
來建立執行緒。終止執行緒:執行緒函式結束後,執行緒自動終止。也可以通過呼叫函式
int pthraad_exit {void *status)
完成終止的操作。執行緒連線:一個執行緒可以等待另一個執行緒的終止, 通過:
int pthread_join (pthread_t thread, void **status__ptr)
來完成。終止執行緒的退出狀態以status_ptr返回。
4.6 執行緒同步
由於執行緒在程序的統一地址空間中執行,所以同一程序的所有執行緒都共享所有全域性變數和資料結構。在多個執行緒都需要修改同一個變數或者是資料結構的時候就會產生競爭。要防止出現這樣的問題,需要執行緒同步。
最簡單的方法就是加鎖。鎖被稱為互斥量。可以使用
- 靜態的方法,定義互斥量m。
- 動態方法,使用
pthread_mutex_init()
函式,設定互斥屬性。
或者使用條件變數的方法。同樣分為靜態和動態兩種方式。
生產者——消費者問題:一系列生產者和消費者程序共享數量有限的緩衝區。每個緩衝區每次有一個特定的專案。也稱有限緩衝問題。生產者的主要作用是生成一定量的資料放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些資料。
訊號量
在作業系統中我們學習過訊號量的知識。訊號量代表了一個資源的可用程度,是程序同步的一般機制。有P原語和V原語,原語都是原子操作或基本操作。
實踐內容過程、問題解決過程
執行緒程式測試
按照教材給出的例項進行了測試。測試內容為使用執行緒進行快速排序。原理為:主執行緒先執行,然後由主執行緒呼叫qsort(&arg),讓qsort()函式執行實現一個N個整數的快速排序。在qsort()中,執行緒會選擇一個基準元素,然後進行快速排序的操作。
程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
typedef struct{
int upperbound;
int lowerbound;
}PARM;
#define N 10
int a[N]={5,1,6,4,7,2,9,8,0,3};// unsorted data
int print(){//print current a[] contents
int i;
printf("[");
for(i=0;i<N;i++)
printf("%d ",a[i]);
printf("]\n");
}
void *Qsort(void *aptr){
PARM *ap, aleft, aright;
int pivot, pivotIndex,left, right,temp;
int upperbound,lowerbound;
pthread_t me,leftThread,rightThread;
me = pthread_self();
ap =(PARM *)aptr;
upperbound = ap->upperbound;
lowerbound = ap->lowerbound;
pivot = a[upperbound];//pick low pivot value
left = lowerbound - 1;//scan index from left side
right = upperbound;//scan index from right side
if(lowerbound >= upperbound)
pthread_exit (NULL);
while(left < right){//partition loop
do{left++;} while (a[left] < pivot);
do{right--;}while(a[right]>pivot);
if (left < right ) {
temp = a[left];a[left]=a[right];a[right] = temp;
}
}
print();
pivotIndex = left;//put pivot back
temp = a[pivotIndex] ;
a[pivotIndex] = pivot;
a[upperbound] = temp;
//start the "recursive threads"
aleft.upperbound = pivotIndex - 1;
aleft.lowerbound = lowerbound;
aright.upperbound = upperbound;
aright.lowerbound = pivotIndex + 1;
printf("%lu: create left and right threadsln", me) ;
pthread_create(&leftThread,NULL,Qsort,(void * )&aleft);
pthread_create(&rightThread,NULL,Qsort,(void *)&aright);
//wait for left and right threads to finish
pthread_join(leftThread,NULL);
pthread_join(rightThread, NULL);
printf("%lu: joined with left & right threads\n",me);
}
int main(int argc, char *argv[]){
PARM arg;
int i, *array;
pthread_t me,thread;
me = pthread_self( );
printf("main %lu: unsorted array = ", me);
print( ) ;
arg.upperbound = N-1;
arg. lowerbound = 0 ;
printf("main %lu create a thread to do QS\n" , me);
pthread_create(&thread,NULL,Qsort,(void * ) &arg);//wait for Qs thread to finish
pthread_join(thread,NULL);
printf ("main %lu sorted array = ", me);
print () ;
}
出現問題與解決辦法
使用常規方法進行編譯,發現無法進行編譯,提示pthread相關函式未定義:
後查閱資料並仔細閱讀,發現需要在編譯時,在gcc命令中加入-pthread
引數,如gcc thread.c -pthread
才可以完成編譯操作。
測試結果
在openEuler中進行測試:
最後程式測試成功。
程式碼連結
程式碼包括一些以前的程式碼,在碼雲。連結:https://gitee.com/Ressurection20191320/code/tree/master/IS