和多進程相比,多線程的最大特點就是資源的共享。然而共享卻涉及到一個同步的問題,這是多線程編程的難點。Linux系統提供了多種方式處理線程間的同步問題,主要有互斥鎖、條件變量和異步信號。本文先講互斥鎖。
互斥鎖通過鎖機制來實現線程間的同步。在同一個時刻,只允許一個線程執行一個關鍵部分的代碼。
使用互斥鎖前必須對其進行初始化。有以下2種方式:
- 將宏結構常量PTHREAD_MUTEX_INITIALIZER賦給互斥鎖。pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER。
- 另外是通過pthread_mutex_init函數初始化互斥鎖。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
mutexattr表示互斥鎖的屬性,如果爲NULL使用默認屬性。
初始化後,就可以給互斥鎖加鎖了。加鎖的2個函數是:pthread_mutex_lock()和pthread_mutex_trylock()。它們的原型如下:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
用lock加鎖時,如果mutex已經被鎖住,當前嘗試加鎖的線程就會阻塞,直到互斥鎖被其他線程釋放。當加鎖函數返回時,則說明互斥鎖已經被當前線程成功加鎖。trylock函數則不同,如果mutex已經被加鎖,它會立即返回,錯誤碼爲EBUSY,而不會阻塞。
pthread_mutex_unlock用來解鎖,其原型爲:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解鎖要滿足兩個條件:一是互斥鎖必須處於加鎖的狀態;二是解鎖的線程也必須是當初加鎖的線程。解鎖後如果有其他線程在等待互斥鎖,則等待隊列中的第一個線程將獲得互斥鎖。
互斥鎖使用完畢後,必須進行清除。清除互斥鎖使用函數pthread_mutex_destroy,該函數原型爲:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
清除鎖時要求鎖處於開放的狀態,清除成功返回0;否則函數返回EBUSY。
以下例子演示通過互斥鎖對全局變量在2個線程之間進行同步:
#include <stdio.h>
#include <pthread.h>
//聲明鎖和全局變量
pthread_mutex_t number_mutex;
int globalnumber;
//線程1
void thread1()
{
//對全局變量加值5次
int i=5;
while(i-->0){
pthread_mutex_lock(&number_mutex);//加鎖
globalnumber++;
printf("Thread 1 added,globalnumber= %d\n",globalnumber);
pthread_mutex_unlock(&number_mutex);//解鎖
sleep(1);
}
}
//線程2
void thread2()
{
//對全局變量加值3次
int i=3;
while(i-->0){
pthread_mutex_lock(&number_mutex);//加鎖
globalnumber++;
printf("Thread 2 added,globalnumber= %d\n",globalnumber);
pthread_mutex_unlock(&number_mutex);//解鎖
sleep(2);
}
}
main()
{
pthread_t thid1,thid2;
printf("This is Main Thread.\n");
//互斥鎖的初始化
pthread_mutex_init(&number_mutex,NULL);
pthread_create(&thid1,NULL,thread1,NULL);
pthread_create(&thid2,NULL,thread2,NULL);
int status1,status2;
pthread_join(thid1,(void*)&status1);
pthread_join(thid2,(void*)&status2);
//互斥鎖的清除
pthread_mutex_destroy(&number_mutex);
//等待上述2個子線程運行結束,最後再講2個子線程操作完畢的全局變量打印到屏幕
printf("globalnumber is: %d\n",globalnumber);
printf("Main Thread exit\n");
}
運行結果: