1. 程式人生 > >Linux基礎實訓(利用執行緒和互斥鎖)

Linux基礎實訓(利用執行緒和互斥鎖)

                     實驗要求(linux)
1 定義一個長度為8的陣列
2 輸入不同的8個(大寫)字母(ASDFGHJK)
3 對字串進行排序

4 用執行緒和互斥鎖輸出按順序輸出AAAAAAADDDDDDDD........SSSSSSSS.
提示:
for(...A..H)
{
...
pthread.create(function,arg) ;
}

/*pthread.c*/
#include<stdio.h>
#include<string.h>
#include<pthread.h> 
#include<unistd.h>
#include"sort.h"
#define MAX 20
int n ; // 全域性變數 數字的個數  
pthread_mutex_t mutex ; // 互斥區
void *thread(char *str)
{
    char s = *(char*) str ; 	
    pthread_mutex_lock(&mutex)  ;
    for(int i = 0 ; i<n; i++ )
    printf("%c",s) ;
    pthread_mutex_unlock(&mutex) ; 
    return NULL ; 
}
int main()
{
    pthread_t id[10] ; 
    int res  ; 
    char buf[10] ; 
    int len ; 
    pthread_mutex_init(&mutex,NULL) ; // 初始化互斥
    printf("Please input 8 letters : \n ") ; 
    scanf("%s",buf) ;
    len = strlen(buf) ;
    n = len  ; 
    bubble_sort(buf,len) ; 
    printf("%s\n" ,buf) ; // 輸出排好序的
    for(int i = 0 ; i<len ; i++ ) 
    {
    	res = pthread_create(&id[i], NULL ,(void *)thread ,&buf[i]);
    	if(res!=0)
        {
           printf("Creat thread error \n" ) ; 
           return -1 ; 
        }
	
    	pthread_join(id[i],NULL) ;//呼叫執行緒阻塞

   }
    pthread_mutex_destroy(&mutex) ; // 離開互相斥區

    return 0 ;
}
#include "sort.h"
void bubble_sort(char str[] ,int n )  
{
	char tmp ; 
	for(int i = 0 ; i <n-1 ; i++) 
	{
		for(int j = 0  ;j<n-i-1 ; j++) 
		{
			if(str[j] > str[j+1]) 
			{
				tmp = str[j] ; 
				str[j] = str[j+1] ; 
				str[j+1] = tmp ; 
			}
		

		}
	}
	return ; 
}

 

#ifndef _SORT_H
#define _SORT_H
void bubble_sort(char a[],int n )  ;
#endif
#makefile
pthread: pthread.o sort.o
	gcc -o pthread pthread.o sort.o -lpthread
pthread.o: pthread.c sort.h
	gcc -c pthread.c
sort.o: sort.c sort.h
	gcc -c sort.c
clean:
	rm *.o

 

Linux多執行緒程式設計(一)---多執行緒基本程式設計

    執行緒是指執行中的程式的排程單位。一個執行緒指的是程序中一個單一順序的控制流,也被稱為輕量級執行緒。它是系統獨立排程和分配的基本單位。同一程序中的多個執行緒將共享該系統中的全部系統資源,比如檔案描述符和訊號處理等。一個程序可以有很多執行緒,每個執行緒並行執行不同的任務。

執行緒與程序比較

   ①  和程序相比,它是一種非常“節儉”的多工操作方式。在Linux系統中,啟動一個新的程序必須分配給它獨立的地址空間,建立眾多的資料表來維護其程式碼段、堆疊段和資料段,這種多工工作方式的代價非常“昂貴”。而運行於一個程序中的多個執行緒,它們彼此之間使用相同的地址空間,共享大部分資料,啟動一個執行緒所花費的空間遠遠小於啟動一個程序所花費的空間,而且執行緒間彼此切換所需要時間也遠遠小於程序間切換所需要的時間。

   ②  執行緒間方便的通訊機制。對不同程序來說它們具有獨立的資料空間,要進行資料的傳遞只能通過通訊的方式進行。這種方式不僅費時,而且很不方便。執行緒則不然,由於同一程序下的執行緒之間共享資料空間,所以一個執行緒的資料可以直接為其他執行緒所用,不僅方便,而且快捷。

執行緒基本程式設計

   Linux系統下的多執行緒遵循POSIX執行緒介面,稱為pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連線時需要使用庫libpthread.a。因為pthread的庫不是Linux系統的庫,所以在編譯時要加上 -lpthread。例如:gcc  filename  -lpthread。注意,這裡要講的執行緒相關操作都是使用者空間中的執行緒的操作。

   執行緒建立:建立執行緒實際上就是確定呼叫該執行緒函式的入口點,這裡通常使用的函式是pthread_create()。線上程建立後,就開始執行相關的執行緒函式。

  

  執行緒退出:線上程建立後,就開始執行相關的執行緒函式,在該函式執行完之後,該執行緒也就退出了,這也是執行緒退出的一種方法。另一種退出執行緒的方法是使用函式pthread_exit(),這是執行緒的主動行為。這裡要注意的是,在使用執行緒函式時,不能隨意使用exit()退出函式來進行出錯處理。由於exit()的作用是使呼叫程序終止,而一個程序往往包含多個執行緒,因此,在使用exit()之後,該程序中的所有執行緒都終止了。線上程中就可以使用pthread_exit()來代替程序中的exit()。

  

  執行緒等待:由於一個程序中的多個執行緒是共享資料段的,因此,通常線上程退出後,退出執行緒所佔用的資源並不會隨著執行緒的終止而得到釋放。正如程序之間可以用wait()系統呼叫來同步終止並釋放資源一樣,執行緒之間也有類似機制,那就是pthread_join()函式。pthread_join()用於將當前程序掛起來等待執行緒的結束。這個函式是一個執行緒阻塞的函式,呼叫它的函式將一直等待到被等待的執行緒結束為止,當函式返回時,被等待執行緒的資源就被收回。

  

  執行緒取消:前面已經提到執行緒呼叫pthread_exit()函式主動終止自身執行緒,但是在很多執行緒應用中,經常會遇到在別的執行緒中要終止另一個執行緒的問題,此時呼叫pthread_cancel()函式來實現這種功能,但在被取消的執行緒的內部需要呼叫pthread_setcancel()函式和pthread_setcanceltype()函式設定自己的取消狀態。例如,被取消的執行緒接收到另一個執行緒的取消請求之後,是接受函式忽略這個請求;如果是接受,則再判斷立刻採取終止操作還是等待某個函式的呼叫等。

  

  執行緒識別符號獲取:獲取呼叫執行緒的標識ID。

  

  執行緒清除:執行緒終止有兩種情況:正常終止和非正常終止。執行緒主動呼叫pthread_exit()或者從執行緒函式中return都將使執行緒正常退出,這是可預見的退出方式;非正常終止是執行緒在其它執行緒的干預下,或者由於自身執行出錯(比如訪問非法地址)而退出,這種退出方式是不可預見的。不論是可預見的執行緒終止還是異常終止,都回存在資源釋放的問題,如何保證執行緒終止時能順利地釋放掉自己所佔用的資源,是一個必須考慮的問題。

  從pthread_cleanup_push()的呼叫點到pthread_cleanup_pop()之間的程式段中的終止動作(包括呼叫pthread_exit()和異常終止,不包括return)都將執行pthread_cleanup_push()所指定的清理函式。

  

   

實驗1

   功能:使用pthread_create()函式建立執行緒的例項

   程式碼:thread_create.c檔案

    

     

    編譯:使用命令:gcc thread_create.c -o thread_create -lpthread編譯,注意不要忘了加 -lpthread,否則會出現如下的錯誤

/tmp/ccjfZIN3.o: In function `main':
thread_create.c:(.text+0x8b): undefined reference to `pthread_create'
thread_create.c:(.text+0xc0): undefined reference to `pthread_create'
thread_create.c:(.text+0xeb): undefined reference to `pthread_join'
thread_create.c:(.text+0xfc): undefined reference to `pthread_join'
collect2: ld returned 1 exit status

   執行:

   

實驗2

  功能:使用pthread_exit()函式退出執行緒的舉例

  程式碼:thread_exit.c檔案

     

  編譯:gcc thread_exit.c -o thread_exit -lpthread

  執行:./thread_exit

  

實驗3

  功能:用pthread_join()實現執行緒等待。

  程式碼:thread_join.c檔案

  

  編譯:gcc thread_join.c -o thread_join -lpthread  

  執行:./thread_join

  

 可以看出,pthread_join()等到執行緒結束後,程式才繼續執行。

實驗4

  功能:使用pthread_self()獲取執行緒ID

  程式碼:thread_id.c檔案

  

  編譯:gcc thread_id.c -o thread_id -lpthread

  執行:./thread_id

  

實驗5

  功能:執行緒清理函式的使用

  程式碼:thread_clean.c

  

  

   

   注意,在編寫的程式碼的時候,自己修改一下傳遞的引數和clean_pop函式的引數,相信你會更有收穫。

  編譯:gcc thread_clean.c -o thread_clean -lpthread

  執行:./thread_clean

   

實驗6

  功能:本實驗建立了3個程序,為了更好的描述執行緒之間的並行執行,讓3個執行緒共用同一個執行函式。每個執行緒都有5次迴圈(可以看成5個小任務),每次迴圈之間會隨機等待1~10s的時間,意義在於模擬每個任務的到達時間是隨機的,並沒有任何特定的規律。

  程式碼:thread.c檔案,該程式碼檔案點此下載

  

  

  編譯:gcc thread.c -o thread -lpthread

  執行:./thread

    

  從實驗結果可以看出,執行緒執行的順序雜亂無章的,看著就頭疼,下一節就利用執行緒之間的同步與互斥機制處理此檔案

 

執行緒的最大特點是資源的共享性,但資源共享中的同步問題是多執行緒程式設計的難點。linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數和訊號量。

一、互斥鎖(mutex)

通過鎖機制實現執行緒間的同步。

  • 初始化鎖。在Linux下,執行緒的互斥量資料型別是pthread_mutex_t。在使用前,要對它進行初始化。
    靜態分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    動態分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  • 加鎖。對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,呼叫執行緒會阻塞,直到互斥量被解鎖。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 解鎖。在完成了對共享資源的訪問後,要對互斥量進行解鎖。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 銷燬鎖。鎖在是使用完成後,需要進行銷燬以釋放資源。
    int pthread_mutex_destroy(pthread_mutex *mutex);

 

 

 

 

二、條件變數(cond)

互斥鎖不同,條件變數是用來等待而不是用來上鎖的。條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。條件變數分為兩部分: 條件和變數。條件本身是由互斥量保護的。執行緒在改變條件狀態前先要鎖住互斥量。條件變數使我們可以睡眠等待某種條件出現。條件變數是利用執行緒間共享的全域性變數進行同步的一種機制,主要包括兩個動作:一個執行緒等待"條件變數的條件成立"而掛起;另一個執行緒使"條件成立"(給出條件成立訊號)。條件的檢測是在互斥鎖的保護下進行的。如果一個條件為假,一個執行緒自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個執行緒改變了條件,它發訊號給關聯的條件變數,喚醒一個或多個等待它的執行緒,重新獲得互斥鎖,重新評價條件。如果兩程序共享可讀寫的記憶體,條件變數可以被用來實現這兩程序間的執行緒同步。

  • 初始化條件變數。
    靜態態初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    動態初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  • 等待條件成立。釋放鎖,同時阻塞等待條件變數為真才行。timewait()設定等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個執行緒wait)
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  • 啟用條件變數。pthread_cond_signal,pthread_cond_broadcast(啟用所有等待執行緒)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有執行緒的阻塞
  • 清除條件變數。無執行緒等待,否則返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);

 

 

 

 

 

三、訊號量(sem)

如同程序一樣,執行緒也可以通過訊號量來實現通訊,雖然是輕量級的。訊號量函式的名字都以"sem_"打頭。執行緒使用的基本訊號量函式有四個。

  • 訊號量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    這是對由sem指定的訊號量進行初始化,設定好它的共享選項(linux 只支援為0,即表示它是當前程序的區域性訊號量),然後給它一個初始值VALUE。
  • 等待訊號量。給訊號量減1,然後等待直到訊號量的值大於0。
    int sem_wait(sem_t *sem);
  • 釋放訊號量。訊號量值加1。並通知其他等待執行緒。
    int sem_post(sem_t *sem);
  • 銷燬訊號量。我們用完訊號量後都它進行清理。歸還佔有的一切資源。
    int sem_destroy(sem_t *sem);