多執行緒相關函式歸納
多執行緒相關函式必備標頭檔案:
#include <pthread.h>
#include <semaphore.h>
Linux 的執行緒是通過使用者級的函式庫實現的,一般採用 pthread 執行緒庫實現執行緒的訪問和控制。它用第 3 方posix 標準的 pthread,具有良好的可移植性。 所以,編譯的時候要在後面加上 -lpthread。 比如,編譯client.c生成client可執行檔案:
gcc -o client client.c -lpthread
1.執行緒
1.1執行緒的建立
int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);
功能: 用來建立執行緒。 引數 : thread :傳出引數,儲存新執行緒的標識; attr :一個結構體指標,結構中的元素分別指定新執行緒的執行屬性;但通常傳入為 NULL 即可。 start_routine:是一個函式指標,指向新執行緒的入口點函式(執行執行緒的時候要執行的函式名)。 arg:用於傳遞給第 3 個引數指向的入口點函式的引數,可以為 NULL,表示不傳遞。 返回值:成功,則返回新執行緒的標識;失敗,則返回-1。 1.2執行緒退出
void pthread_exit(void *retval);
作用;表示執行緒的退出。其引數可以被其它執行緒用 pthread_join 函式捕獲。 引數: retval:作為執行緒結束的返回值,會被後面的pthread_join函式捕獲。可以為NULL,或者其他數字。 1.3執行緒的等待
int pthread_join(pthread_t pthid, void **thread_return);
函式功能: 用於將當前執行緒掛起,等待某個執行緒的結束 引數: Pthid:是某一個執行緒的ID。 thread_return:是一個傳出引數,接收執行緒函式的返回值。 返回值:獲取成功,返回0。失敗, 返回負數。
例子:建立一個程序,並且結束它。
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <sys/stat.h> void * fun(void *p) { int i; for(i = 1; i<10; i++) { printf("world,p = %d \n", p); sleep(1); if(i=8) { pthread_exit(200);//迴圈8次時,建立的程序會退出,如果沒有這個函式,該程序會在10個迴圈後自然退出 } } } int main() { pthread_t id; int a, i; pthread_create(&id, NULL,fun,(void *) 123);//建立一個子執行緒,該執行緒函式名為fun,傳入的引數為123. for(i = 1; i<10; i++) { printf("hello \n");//這個是一個父程序,表示與fun這個子執行緒同時進行的。 sleep(1); } pthread_join(id, &a);//如果父執行緒的執行次數比子執行緒的要少 //當父執行緒結束的時候,如果沒有 pthread_join函式等待子執行緒執行的話,父執行緒會退出,而主執行緒的退出會導致程序的退出,故子執行緒也會退出。 printf("a = %d\n", a); return 0; }
2.執行緒的互斥鎖
2.1建立和銷燬互斥鎖 建立互斥鎖有2種方式: 1)靜態方式:使用相應的巨集來初始化互斥鎖; POSIX 定義了一個巨集 PTHREAD_MUTEX_INITIALIZER 來靜態初始化互斥鎖,方法如下:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2)動態方式:使用相應的函式來初始化互斥鎖;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
功能: 初始化互斥鎖。 引數: Mutex:互斥鎖結構。 Mutexattr:用於指定互斥鎖屬性(型別),通常為 NULL標識使用預設屬性。 返回值:成功返回0,失敗返回負數。
2.2登出互斥鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:登出一個互斥鎖 引數: Mutex:要登出的互斥鎖結構 返回值:成功返回0,失敗返回負數。 特別說明:銷燬一個互斥鎖即意味著釋放它所佔用的資源,且要求鎖當前處於開放狀態。
2.3鎖的種類
typedef struct
{
int __mutexkind;
} pthread_mutexattr_t;
int __mutexkind | 鎖的型別 | 描述 |
---|---|---|
PTHREAD_MUTEX_TIMED_NP(NULL) | 普通鎖(快速鎖) | 當一個執行緒加鎖以後,其餘請求鎖的執行緒將形成一個阻塞等待佇列,並在解鎖後按優先順序獲得鎖。這種鎖策略保證了資源分配的公平性 |
PTHREAD_MUTEX_RECURSIVE_NP | 巢狀鎖(遞迴鎖) | 允許同一個執行緒對同一個鎖成功獲得多次,並通過多次 unlock 解鎖。如果是不同執行緒請求,則在加鎖執行緒解鎖時重新競爭。 |
PTHREAD_MUTEX_ERRORCHECK_NP | 檢錯鎖 | 如果同一個執行緒請求同一個鎖,則返回 EDEADLK。否則與 PTHREAD_MUTEX_TIMED_NP 型別動作相同。這樣就保證當不允許多次加鎖時不會出現最簡單情況下的死鎖。 |
例子:初始化為檢錯鎖
pthread_mutex_t lock;
pthread_mutexattr_t mutexattr={PTHREAD_MUTEX_ERRORCHECK_NP};
pthread_mutex_init(&lock, &mutexattr);
2.4鎖的操作 1)加鎖
int pthread_mutex_lock(pthread_mutex_t *mutex)
函式功能:給互斥鎖加鎖 引數: Mutex:要加鎖的互斥鎖結構 返回值:成功返回0,否則返回負數 說明:對於普通鎖,解鎖者可以是同進程內任何執行緒;而檢錯鎖和巢狀鎖則必須由加鎖者解鎖才有效,否則返回 EPERM;在同一程序中的執行緒,如果加鎖後沒有解鎖,則任何其他執行緒都無法再獲得鎖。
2)解鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex)
函式功能:給互斥鎖解鎖 引數: Mutex:要解鎖的互斥鎖。 返回值:成功返回0,否則返回負數 說明:根據不同的鎖型別,可實現不同的行為: 對於快速鎖, pthread_mutex_unlock 解除鎖定; 對於遞規鎖, pthread_mutex_unlock 使鎖上的引用計數減 1; 對於檢錯鎖,如果鎖是本執行緒鎖定的,則解除鎖定,否則什麼也不做。
3)測試加鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex)
函式功能:給一個互斥鎖測試加鎖。與 pthread_mutex_lock()類似,如果互斥鎖未被上鎖則對其上鎖,但不同的是在鎖已經被佔據時返回 EBUSY 而不是掛起等待。 引數: Mutex:要測試加鎖的互斥鎖結構 返回值:成功返回0,否則返回負數
3)銷燬鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex)
函式功能:銷燬互斥鎖,回到初始化鎖前的狀態。 引數: Mutex:要測試加鎖的互斥鎖結構 返回值:成功返回0,否則返回負數
例子:火車售票並且對票進行加鎖
#include <stdio.h>
#include <pthread.h>
int ticketcount = 11;//票數為11
pthread_mutex_t lock;//1:定義一個全域性的 pthread_mutex_t lock;
void* salewinds1(void* args)
{
while(1)
{
pthread_mutex_lock(&lock); //3:在子執行緒函式中呼叫 pthread_mutex_lock 加鎖。
if(ticketcount > 0){ //如果有票
printf("windows1 start sale ticket!the ticket is:%d\n", ticketcount);
sleep(2);
ticketcount --;
printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
}
else
{
pthread_mutex_unlock(&lock);//4:在子執行緒函式中呼叫 pthread_mutex_unlock 解鎖。
pthread_exit(NULL);//退出子執行緒
}
pthread_mutex_unlock(&lock);
sleep(1); //要放到鎖的外面,讓另一個有時間鎖
}
}
void* salewinds2(void* args)
{
while(1)
{
pthread_mutex_lock(&lock);//3:在子執行緒函式中呼叫 pthread_mutex_lock 加鎖。
if(ticketcount>0)
{
printf("windows2 start sale ticket!the ticket is:%d\n",ticketcount);
sleep(2);
ticketcount --;
printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
}
else
{
pthread_mutex_unlock(&lock);//4:在子執行緒函式中呼叫 pthread_mutex_unlock 解鎖。
pthread_exit(NULL);//退出子執行緒
}
pthread_mutex_unlock(&lock);
sleep(1);
}
}
int main()
{
pthread_t pthid1 = 0;
pthread_t pthid2 = 0;
pthread_mutex_init(&lock,NULL); //2:初始化一個互斥鎖,其處於被解鎖的狀態,可以被上鎖。
pthread_create(&pthid1,NULL,salewinds1,NULL);//建立售票執行緒1
pthread_create(&pthid2,NULL,salewinds2,NULL);//建立售票執行緒2
pthread_join(pthid1,NULL);//等待執行緒1,以免主執行緒跑完出去了導致子執行緒自動結束
pthread_join(pthid2,NULL);//等待執行緒2,以免主執行緒跑完出去了導致子執行緒自動結束
pthread_mutex_destroy(&lock); //5:銷燬鎖
return 0;
}
3.執行緒的條件變數
3.1條件變數的建立和登出 1)靜態建立方式 使 PTHREAD_COND_INITIALIZER 常量,如下:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2)動態建立方式
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能:動態初始化條件變數 引數: Cond:條件變數結構 cond_attr:通常為NULL。 返回值:成功返回0,失敗返回-1 3)條件變數登出
int pthread_cond_destroy(pthread_cond_t *cond);
功能:登出一個條件變數 引數: Cond:要登出的條件變數結構。 返回值:成功返回0,失敗返回-EBUSY 說明:只有在沒有執行緒在該條件變數上等待的時候才能登出這個條件變數。 4)無條件等待函式
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
功能:執行緒解開mutex指向的鎖並被條件變數cond阻塞,無條件的等待cond訊號的出現就會返回。 引數: Cond:要等待的條件變數 Mutex:相對應的互斥鎖 返回值 成功返回0,失敗返回負數。
5)計時等待函式
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
功能:執行緒解開mutex指向的鎖並被條件變數cond阻塞,計時等待cond訊號的出現就會返回。經歷 abstime 段時間後,即使條件變數不滿足,阻塞也被解除。 引數: Cond:要等待的條件變數 Mutex:相對應的互斥鎖 Abstime:與time()系統呼叫相同意義的絕對時間形式出現,0表示格林尼治時間1970年1月1日0時0分0秒。 返回值:成功返回0,超時返回ETIMEOUT,失敗返回負數。
6)條件變數激發 激發條件有兩種形式:
int pthread_cond_signal(pthread_cond_t *cond)//按入隊順序啟用一個等待該條件的執行緒
int pthread_cond_broadcast(pthread_cond_t *cond)//啟用所有等待執行緒。
功能:執行緒解開mutex指向的鎖並被條件變數cond阻塞,計時等待cond訊號的出現就會返回。經歷 abstime 段時間後,即使條件變數不滿足,阻塞也被解除。 引數: Cond:要等待的條件變數 返回值:成功返回0,超時返回ETIMEOUT,失敗返回負數。
例子:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex; //定義了一個互斥鎖變數
pthread_cond_t cond; //定義了一個條件變數
void * child1(void *arg)
{
printf("執行緒 1 開始執行! \n");
printf("執行緒 1 上鎖了: %d\n", pthread_mutex_lock(&mutex));//設定互斥鎖為上鎖
printf("執行緒 1 開始等待條件被啟用\n");
pthread_cond_wait(&cond,&mutex); //等待父執行緒傳送訊號
printf("條件滿足後,停止阻塞! \n");
pthread_mutex_unlock(&mutex); //解鎖
return 0;
}
int main(void)
{
pthread_t tid1;
printf("開始測試條件變數! \n");
pthread_mutex_init(&mutex,NULL);//初始化互斥鎖,普通鎖
pthread_cond_init(&cond,NULL);//初始化條件變數
pthread_create(&tid1,NULL,child1,NULL);//建立執行緒child1
sleep(5); //延遲
pthread_cond_signal(&cond); //啟用條件,結束等待
sleep(3); //排程子執行緒
pthread_cond_destroy(&cond); //登出條件變數
pthread_mutex_destroy(&mutex); //登出互斥鎖變數
return 0;
}
4.執行緒的訊號燈
1)訊號燈的建立
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:建立訊號燈。 Sem:這個是個傳出值,傳出訊號燈的標識,用於以下點燈、滅燈操作。 pshared :是否為多程序共享而不僅僅是用於一個程序之間的多執行緒共享。通常為 0.表示執行緒間。 value :為訊號燈的初值。 成功返回0,失敗返回負數。
2)訊號燈的登出
int sem_destroy(sem_t * sem);
功能 登出一個訊號燈。 引數: Sem:要登出訊號燈的標識。 返回值:成功返回0,失敗返回負數。
3)點燈 int sem_post(sem_t * sem); //相當於解鎖 功能:將訊號燈值加 1,表示增加一個可訪問的資源。訊號燈值大於 0,才能訪問公共資源。 引數 Sem:要操作的訊號燈 返回值:成功返回0,否則返回負數 4)滅燈 int sem_wait(sem_t * sem); //相當於加鎖 int sem_trywait(sem_t * sem); 功能: sem_wait:主要用來阻塞當前執行緒,等待燈亮(訊號燈值大於 0),然後將訊號燈原子地減 1,並返回。 sem_trywait:為 sem_wait的非阻塞版,如果訊號燈計數大於 0,則原子地減 1 並返回 0,否則立即返回-1, 引數:Sem:要操作訊號燈的標識. 返回值:成功返回0,否則返回負數 5)獲取燈值
int sem_getvalue(sem_t * sem, int * sval);
功能:獲取訊號燈的值。 引數: Sem:要獲取的訊號燈。 Sval:將值存放在這個指標所致的地址下。 返回值:成功獲取返回0,否則返回負數。 例子:
#include<pthread.h>
#include<stdio.h>
#include <semaphore.h> //標頭檔案包含
int ticketcount = 10;
sem_t lock;
void *chk1(void *args)
{
while(1){
sem_wait(&lock); //因為要訪問全域性的共享變數 ticketcount,所以就要加鎖
if(ticketcount > 0){ //如果有票
printf("windows1 start sale ticket!the ticket is:%d\n",
ticketcount);
ticketcount --; //則賣出一張票
sleep(3);
printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
}
else { //如果沒有票了,就解鎖退出
sem_post(&lock);
break;
}
sem_post(&lock);
sleep(1); //要放到鎖的外面
}
pthread_exit(NULL);
}
void *chk2(void *args)
{
while(1){
sem_wait(&lock); //因為要訪問全域性的共享變數 ticketcount,所以就要加鎖
if(ticketcount > 0){ //如果有票
printf("windows2 start sale ticket!the ticket is:%d\n",
ticketcount);
ticketcount --; //則賣出一張票
sleep(3);
printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
}
else{ //如果沒有票了,就解鎖退出
sem_post(&lock);
break;
}
sem_post(&lock);
sleep(1); //要放到鎖的外面
}
pthread_exit(NULL);
}
main()
{
pthread_t pthid1,pthid2;
sem_init(&lock,0,1); //訊號燈值初始為 1,表示資源可用
pthread_create(&pthid1,NULL,chk1,NULL);
pthread_create(&pthid2,NULL,chk2,NULL);
pthread_join(pthid1,NULL);
pthread_join(pthid2,NULL);
sem_destroy(&lock);
}