LINUX - pthread_mutex_lock

原文連接:http://www.javashuo.com/article/p-ottseomz-ea.html

互斥的概念

在多線程編程中,引入了對象互斥鎖的概念,來保證共享數據操做的完整性。 每一個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻, 只能有一個線程訪問該對象。html

互斥鎖操做

互斥鎖也能夠叫線程鎖,接下來講說互斥鎖的的使用方法。編程

對互斥鎖進行操做的函數,經常使用的有以下幾個:安全

複製代碼
#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

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_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
複製代碼

 

對線程鎖進行操做的函數有不少,還包括許多線程鎖屬性的操做函數, 不過通常來講,對於並不複雜的狀況, 只須要使用建立、獲取鎖、釋放鎖、刪除鎖這幾個就足夠了。多線程

建立互斥鎖

因此下面簡單看一下如何建立和使用互斥鎖。函數

在使用互斥鎖以前,須要先建立一個互斥鎖的對象。 互斥鎖的類型是 pthread_mutex_t ,因此定義一個變量就是建立了一個互斥鎖。測試

pthread_mutex_t mtx;

 

這就定義了一個互斥鎖。可是若是想使用這個互斥鎖仍是不行的,咱們還須要對這個互斥鎖進行初始化, 使用函數 pthread_mutex_init() 對互斥鎖進行初始化操做。ui

//第二個參數是 NULL 的話,互斥鎖的屬性會設置爲默認屬性
pthread_mutex_init(&mtx, NULL);

 

除了使用 pthread_mutex_init() 初始化一個互斥鎖,咱們還可使用下面的方式定義一個互斥鎖:this

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

 

在頭文件 /usr/include/pthread.h 中,對 PTHREAD_MUTEX_INITIALIZER 的聲明以下spa

# define PTHREAD_MUTEX_INITIALIZER \
   { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

 

爲何能夠這樣初始化呢,由於互斥鎖的類型 pthread_mutex_t 是一個聯合體, 其聲明在文件 /usr/include/bits/pthreadtypes.h 中,代碼以下:線程

複製代碼
/* Data structures for mutex handling.  The structure of the attribute
   type is not exposed on purpose.  */
typedef union
{
    struct __pthread_mutex_s
    {
        int __lock;
        unsigned int __count;
        int __owner;
#if __WORDSIZE == 64
        unsigned int __nusers;
#endif
        /* KIND must stay at this position in the structure to maintain
           binary compatibility.  */
        int __kind;
#if __WORDSIZE == 64
        int __spins;
        __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV  1
#else
        unsigned int __nusers;
        __extension__ union
        {
            int __spins;
            __pthread_slist_t __list;
        };
#endif
    } __data;
    char __size[__SIZEOF_PTHREAD_MUTEX_T];
    long int __align;
} pthread_mutex_t;
複製代碼

 

獲取互斥鎖

接下來是如何使用互斥鎖進行互斥操做。在進行互斥操做的時候, 應該先"拿到鎖"再執行須要互斥的操做,不然可能會致使多個線程都須要訪問的數據結果不一致。 例如在一個線程在試圖修改一個變量的時候,另外一個線程也試圖去修改這個變量, 那就極可能讓後修改的這個線程把前面線程所作的修改覆蓋了。

下面是獲取鎖的操做:

阻塞調用

pthread_mutex_lock(&mtx);

 

這個操做是阻塞調用的,也就是說,若是這個鎖此時正在被其它線程佔用, 那麼 pthread_mutex_lock() 調用會進入到這個鎖的排隊隊列中,並會進入阻塞狀態, 直到拿到鎖以後纔會返回。

非阻塞調用

若是不想阻塞,而是想嘗試獲取一下,若是鎖被佔用咱就不用,若是沒被佔用那就用, 這該怎麼實現呢?可使用 pthread_mutex_trylock() 函數。 這個函數和 pthread_mutex_lock() 用法同樣,只不過當請求的鎖正在被佔用的時候, 不會進入阻塞狀態,而是馬上返回,並返回一個錯誤代碼 EBUSY,意思是說, 有其它線程正在使用這個鎖。

複製代碼
int err = pthread_mutex_trylock(&mtx);
if(0 != err) {
    if(EBUSY == err) {
        //The mutex could not be acquired because it was already locked.
    }
}
複製代碼

 

超時調用

若是不想不斷的調用 pthread_mutex_trylock() 來測試互斥鎖是否可用, 而是想阻塞調用,可是增長一個超時時間呢,那麼可使用 pthread_mutex_timedlock() 來解決, 其調用方式以下:

複製代碼
struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL) + 1;
abs_timeout.tv_nsec = 0;

int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if(0 != err) {
    if(ETIMEDOUT == err) {
        //The mutex could not be locked before the specified timeout expired.
    }
}
複製代碼

 

上面代碼的意思是,阻塞等待線程鎖,可是隻等1秒鐘,一秒鐘後若是還沒拿到鎖的話, 那就返回,並返回一個錯誤代碼 ETIMEDOUT,意思是超時了。

其中 timespec 定義在頭文件 time.h 中,其定義以下

struct timespec
{
    __time_t tv_sec;        /* Seconds.  */
    long int tv_nsec;       /* Nanoseconds.  */
};

 

還須要注意的是,這個函數裏面的時間,是絕對時間,因此這裏用 time() 函數返回的時間增長了 1 秒。

釋放互斥鎖

用完了互斥鎖,必定要記得釋放,否則下一個想要得到這個鎖的線程, 就只能去等着了,若是那個線程很不幸的使用了阻塞等待,那就悲催了。

釋放互斥鎖比較簡單,使用 pthread_mutex_unlock() 便可:

pthread_mutex_unlock(&mtx);

 

銷燬線程鎖

經過 man pthread_mutex_destroy 命令能夠看到 pthread_mutex_destroy() 函數的說明, 在使用此函數銷燬一個線程鎖後,線程鎖的狀態變爲"未定義"。有的 pthread_mutex_destroy 實現方式,會使線程鎖變爲一個不可用的值。一個被銷燬的線程鎖能夠被 pthread_mutex_init() 再次初始化。對被銷燬的線程鎖進行其它操做,其結果是未定義的。

對一個處於已初始化但未鎖定狀態的線程鎖進行銷燬是安全的。儘可能避免對一個處於鎖定狀態的線程鎖進行銷燬操做。

銷燬線程鎖的操做以下:

pthread_mutex_destroy(&mtx)
相關文章
相關標籤/搜索