第十一章(二) 執行緒鎖的使用

2021-06-28 11:31:25 字數 3152 閱讀 8774

執行緒同步

當乙個執行緒可以修改的變數,其他執行緒也可以讀取或者修改的時候,我們就需要對這些執行緒進行同步,確保它們訪問變數的儲存內容時不會訪問到無效的值。

互斥量可以使用pthread的互斥介面來保護資料,確保同一時間只有乙個執行緒訪問資料

互斥變數是用 pthread_mutex_t資料型別表示的。

在使用互斥變數之前,需要對它進行初始化,將其設定為常量 pthread_mutex_initializer(只適用於靜態分配的互斥變數), 也可以通過呼叫 pthraed_mutex_init 函式進行初始化。

若動態分配互斥變數,(呼叫malloc),則需要在釋放記憶體前呼叫 pthread_mutex_destroy

函式    int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t attr);

函式    int pthread_mutex_destroy(pthread_mutex_t *mutex);

對互斥量進行加鎖,需要呼叫函式     pthread_mutex_lock。

若互斥量已經上鎖了,呼叫執行緒將會阻塞直到互斥量被解鎖。 對互斥量進行解鎖,需要呼叫 函式       pthread_mutex_unlock.

若不希望執行緒被阻塞,可以呼叫函式    pthread_mutex_trylock嘗試對互斥量進行加鎖

若互斥量未鎖,則將鎖住互斥量,返回0;否則,失敗,返回 ebusy

以下展示了如何使用互斥鎖保護資料結構

#include #include #include struct foo;

struct foo *alloc(int id)

}return fp;

}void hold(struct foo *fp)

void rele(struct foo *fp)

else

}

接下來則展示了如何利用兩個互斥變數,且不產生死鎖:

#include #include #include #define nhash 29

#define hash(id) (((unsigned long)id)%nhash)

//這裡使用雜湊表, 相同的hash key的一些項通過鍊錶鏈結起來

struct foo ;

struct foo *hash[nhash];

pthread_mutex_t hashlock = pthread_mutex_initializer;

struct foo* alloc(int id)

//與上乙個程式一樣,因為這幾個變數是不由使用者修改的,且在初始化的時候因為還沒掛到雜湊表中,所以不可能被其他執行緒看到,因此這裡修改資料就不加鎖

pthread_mutex_lock(&hashlock); //現在我們要修改雜湊表,有可能另乙個執行緒也要在這個位置掛乙個項,所以我們這裡先鎖起來整個雜湊表

xid = hash(id);

fp->f_next = hash[xid];

hash[xid] = fp; //成功掛到雜湊表上

pthread_mutex_lock(&fp->f_lock); //掛上去之後,這個項就很有可能被其他執行緒看到!!! 但接下來我們還要【初始化】一些可以被修改的變數,如果先釋放了hashlock,那麼f_lock鎖可能被其他執行緒先佔到,那樣就在沒有初始化的資料上操作了,會造成異常!因此這裡先鎖住f_lock鎖.但是,要注意,但凡遇到乙個鎖未開,這之前又要鎖另乙個鎖的時候,一定要仔細看是否會造成死鎖,千萬不要導致兩個執行緒互相等待一方鎖開啟! 這裡的 f_lock 可能被其他執行緒先佔到嗎? 不可能的,因為其他執行緒即使看到了這個項,要持有的話還需要先擁有hashlock鎖!

pthread_mutex_unlock(&hashlock);

//在f_lock保護好這個項後,hashlock就可以釋放,被其他執行緒用了。且暫時不會有人打擾這個項

//......some changes of details

pthread_mutex_unlock(&fp->f_lock);

}return fp;

}void hold(struct foo *fp)

void rele(struct foo *fp)

xid = hash(fp->f_id);

temp = hash[xid];

if(temp == fp)

else

pthread_mutex_unlock(&hashlock);

pthread_mutex_unlock(&fp->f_lock);

pthread_mutex_destroy(&fp->f_lock);

free(fp);

}else

}struct foo* find(int id)

}pthread_mutex_unlock(&hashlock);

return fp;

}

寫下這個程式,當真是十分繁瑣,期間鎖的順序等需要考慮甚多。

我們發現,這裡的鎖鎖的太細緻了,開鎖解鎖可能花費很多時間,所以我們不如精簡一下:

可以看到,以下程式整個鎖的過程變粗了,即鎖的範圍變大了(即有時候允許別人同時執行,但這裡我們直接不允許,直接鎖住)

void hold(struct foo *fp)

void rele(struct foo *fp)

else

pthread_mutex_unlock(&hashlock);

pthread_mutex_destroy(&fp->f_lock);

free(fp);

}else

}struct foo* find(int id)

}pthread_mutex_unlock(&hashlock);

return fp;

}

對比這個程式和上面的程式,我們發現,

如果鎖的粒度太細,過多的鎖開銷會影響系統效能,而且**十分複雜。

但是,如果鎖的粒度太粗,那麼就容易出現很多執行緒阻塞等待相同的鎖,可能並不能改善併發性。

重要的是找到平衡!

apue 第十一章 執行緒

pthread join pthread t tid,void rval ptr old執行緒建立了new執行緒,然後呼叫pthread join來等待new執行緒返回,返回值為 rval ptr apue中提到乙個執行緒的分離狀態概念 模擬於程序,子程序在退出中,會保留退出狀態供父程序呼叫wait...

c primer 第十一章 使用類

一,操作符過載 1 函式過載 多型 名稱相同,特徵標 引數列表 不同的函式。完成相同的基本操作 2 操作符左側的對像是呼叫物件,操作符右側的作為引數被傳遞的物件 3 過載限制 1 過載後的操作符至少有乙個運算元是使用者定義的型別。防止使用者為標準型別過載操作符 2 使用操作符,不能違反操作符原來的句...

第十一章 Nginx使用教程

總結 示例 pandas 是基於numpy 的一種工具,該工具是為了解決資料分析任務而建立的。如下 示例 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns impo...