Linux互斥鎖

互斥的概念

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

互斥鎖操作

互斥鎖也可以叫線程鎖,接下來說說互斥鎖的的使用方法。

對互斥鎖進行操作的函數,常用的有如下幾個:

複製代碼

#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() 對互斥鎖進行初始化操作。

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

 

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

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

 

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

# 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)

 

同步地址:https://www.fengbohello.top/archives/linux-pthread-mutex

 


作  者:郝峯波 
個人網站:http://www.fengbohello.top/ 
E-mail : [email protected] 
歡迎轉載,轉載請註明作者和出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章