Linux應用開發之多執行緒
技術標籤:linux
本文包括執行緒的基本定義和操作以及互斥鎖。
一、執行緒的定義
執行緒包含在程序裡面,一個程序可以有多個執行緒。
做個簡單的比喻:程序=火車,
執行緒=車廂執行緒在程序下行進(單純的車廂無法執行)
一個程序可以包含多個執行緒(一輛火車可以有多個車廂)
不同程序間資料很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點換乘)
同一程序下不同執行緒間資料很易共享(A車廂換到B車廂很容易)
程序要比執行緒消耗更多的計算機資源(採用多列火車相比多個車廂更耗資源)
程序間不會相互影響,一個執行緒掛掉將導致整個程序掛掉(一列火車不會影響到另外一列火車,但如果一列火車上的一節車廂著火了,將影響到所有車廂)程序使用的記憶體地址可以上鎖,即一個執行緒使用某些共享記憶體時,其他執行緒必須等它結束,才能使用這一塊記憶體。(比如火車上的洗手間)-“互斥鎖”
程序使用的記憶體地址可以限定使用量(比如火車上的餐廳,最多隻允許多少人進入,如果滿了需要在門口等,等有人出來了才能進去)-“訊號量”
二、執行緒的基本操作
標頭檔案:#include <pthread.h>
建立執行緒:int pthread_create((pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg))
thread:執行緒識別符號
start_routine:執行緒函式的起始地址
arg:傳遞給 start_routine 的引數
退出執行緒:void pthread_exit(void *retval)
Retval:pthread_exit()呼叫者執行緒的返回值,可由其他函式如 pthread_join 來檢索獲取
等待執行緒結束:int pthread_join((pthread_t th, void **thread_return))
th:等待執行緒的識別符號,等同於建立執行緒pthread_create()中的入口引數thread(執行緒識別符號)
thread_return:使用者定義的指標,用來儲存被等待執行緒的返回值(不為 NULL 時)
線上程中不能隨意使用exit()函式進行出錯處理,exit()是使呼叫的程序終止,該程序中的執行緒也會全部終止。
三、執行緒的屬性attr(attribute)
執行緒是有屬性的,屬性主要包括繫結屬性、分離屬性、堆疊地址、堆疊大小、優先順序。其中系統預設的屬性為非繫結、非分離、預設1M 的堆疊、與父程序同樣級別的優先順序。
繫結屬性:Linux 中採用“一對一”的執行緒機制,也就是一個使用者執行緒對應一個核心執行緒。繫結屬性就是指一個使用者執行緒固定地分配給一個核心執行緒,因為 CPU 時間片的排程是面向核心執行緒(也就是輕量級程序)的,因此具有繫結屬性的執行緒可以保證在需要的時候 總有一個核心執行緒與之對應。而與之相對的非繫結屬性就是指使用者執行緒和核心執行緒的關係不 是始終固定的,而是由系統來控制分配的。
分離屬性 :分離屬性是用來決定一個執行緒以什麼樣的方式來終止自己。在非分離情況下,當一個執行緒結束時,它所佔用的系統資源並沒有被釋放,也就是沒有真正的終止。只有當 pthread_join()函式返回時,建立的執行緒才能釋放自己佔用的系統資源。而在分離屬性情況下,一個執行緒結 束時立即釋放它所佔有的系統資源。這裡要注意的一點是,如果設定一個執行緒的分離屬性,而這個執行緒執行又非常快,那麼它很可能在 pthread_create 函式返回之前就終止了,它終止以 後就可能將執行緒號和系統資源移交給其他的執行緒使用,這時呼叫 pthread_create 的執行緒就得到了錯誤的執行緒號。
執行緒屬性結構體:pthread_attr_t (對應下面函式設定的入口引數attr)
初始化執行緒屬性:int pthread_attr_init(pthread_attr_t *attr)
attr:執行緒屬性
設定繫結屬性:int pthread_attr_setscope(pthread_attr_t *attr, int scope)
attr:執行緒屬性
scope: 繫結:PTHREAD_SCOPE_SYSTEM /非繫結:PTHREAD_SCOPE_PROCESS
設定執行緒分離屬性:int pthread_attr_setscope(pthread_attr_t *attr, int detachstate)
attr:執行緒屬性
detachstate: 分離:PTHREAD_CREATE_DETACHED /非分離:PTHREAD _CREATE_JOINABLE
設定/獲取執行緒優先順序:
int pthread_attr_getschedparam (pthread_attr_t *attr, struct sched_param *param)
int pthread_attr_setschedparam (pthread_attr_t *attr, struct sched_param *param)
attr:執行緒屬性
param:執行緒優先順序
在設定完這些屬性後,就可以呼叫 pthread_create 函式來建立執行緒了。
四、執行緒的例程
使用“free”命令可以檢視記憶體使用情況
#include <stdio.h>
#include <pthread.h>
/*執行緒一*/
void thread1(void)
{
printf("This is a pthread1.\n");
pthread_exit(0);
}
int main(void)
{
pthread_t id1,id2;
int i,ret;
pthread_attr_t attr; /*建立執行緒結構*/
/*初始化執行緒*/
pthread_attr_init(&attr);
/*設定執行緒繫結屬性*/
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
/*設定執行緒分離屬性*/
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
/*建立執行緒一*/
ret=pthread_create(&id1,NULL,(void *) thread1,NULL);
if(ret!=0)
{
printf ("Create pthread error!\n");
exit (1);
}
/*等待執行緒結束*/
pthread_join(id1,NULL);
exit (0);
五、互斥鎖
mutex 是一種簡單的加鎖的方法來控制對共享資源的存取。這個互斥鎖只有兩種狀態,也就是上鎖和解鎖,可以把互斥鎖看作某種意義上的全域性變數。在同一時刻只能有一個執行緒掌握某個互斥上的鎖,擁有上鎖狀態的執行緒能夠對共享資源進行操作。若其他執行緒希望上鎖 一個已經上鎖了的互斥鎖,則該執行緒就會掛起,直到上鎖的執行緒釋放掉互斥鎖為止。可以說, 這把互斥鎖使得共享資源按序在各個執行緒中操作。
互斥鎖結構為pthread_mutex_t,操作主要包括以下幾個步驟。
-
互斥鎖初始化: int pthread_mutex_init pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Mutexattr:建立快速互斥鎖:PTHREAD_MUTEX_INITIALIZER
建立遞迴互斥鎖:PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
建立檢錯互斥鎖:PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP -
互斥鎖上鎖: int pthread_mutex_lock (pthread_mutex_t *mutex,)
-
互斥鎖判斷上鎖:int pthread_mutex_trylock (pthread_mutex_t *mutex,)
-
互斥鎖接鎖: int pthread_mutex_unlock (pthread_mutex_t *mutex,)
-
消除互斥鎖: int pthread_mutex_destroy(pthread_mutex_t *mutex,)
其中,互斥鎖可以分為快速互斥鎖、遞迴互斥鎖和檢錯互斥鎖。這三種鎖的區別主要在於其他未佔有互斥鎖的執行緒在希望得到互斥鎖時的是否需要阻塞等待。快速鎖是指 呼叫執行緒會阻塞直至擁有互斥鎖的執行緒解鎖為止。遞迴互斥鎖能夠成功地返回並且增加 呼叫執行緒在互斥上加鎖的次數,而檢錯互斥鎖則為快速互斥鎖的非阻塞版本,它會立即返回並返回一個錯誤資訊。