最近在學習多執行緒的網路程式設計,互斥量和條件變數是多執行緒程式設計中常用的執行緒同步方式。在編寫自己的高併發伺服器的過程中對互斥量和條件變數進行了封裝,想要測試一下自己封裝的類是否正確,能否通過自己封裝的條件變數類順利實現多執行緒順序列印。
先來簡單的看一下條件變數的常用介面:
int pthread_cond_init
(pthread_cond_t *cond,
const pthread_condattr_t *attr)
;//對條件變數進行初始化,條件變數的屬性引數由attr確定,如果使用預設引數,attr一般為null;
int pthread_cond_destroy
(pthread_cond_t *cond)
;//解除條件變數,不過並不對條件變數的記憶體;
int pthread_cond_wait
(pthread_cond_t *cond, pthread_mutex_t *mutex)
;int pthread_cond_timedwait
(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abtime)
;//阻塞,等待被喚醒,該介面較複雜,後面會詳細介紹。pthread_cond_timewait介面增加了定時功能,一旦時間超過abtime,則不再繼續阻塞。
int pthread_cond_signal
(pthread_cond_t *cond)
;int pthread_cond_broadcast
(pthread_cond_t *cond)
;//喚醒阻塞於該條件變數的執行緒。signal只喚醒乙個執行緒,broadcast則喚醒所有阻塞於該條件變數的執行緒
條件變數一般需要配合互斥量使用,下面配合一段偽**來解釋其工作方式:
偽**如下:
執行緒a: 執行緒b:
pthread_mutex_lock(); pthread_mutex_lock();
pthread_cond_wait(); pthread_cond_signal();
…//執行** pthread_mutex_unlock;
pthread_mutex_unlock();
其具體的工作流程如下:
步驟執行緒a
執行緒b(1)
對臨界區進行加鎖
(2)呼叫pthread_cond_wait阻塞該執行緒,等待被喚醒
(3)pthread_cond_wait釋放掉鎖
(4)獲的互斥鎖
(5)呼叫pthread_cond_signal喚醒阻塞於該條件變數的執行緒
(6)(此時執行緒已經喚醒,但是需要重新獲取互斥鎖才能繼續執行,所以仍然阻塞)
釋放互斥鎖
(7)pthread_cond_wait()重新獲得互斥鎖,繼續執行
所以pthread_cond_wait需要先釋放掉擁有的互斥量之後再休眠。被喚醒之後需要重新獲取互斥量之後才能繼續執行。
弄清楚了這個之後,我們來看一下條件變數的常用方式,給定乙個場景:執行緒a和b從佇列q中取資料,執行緒c向佇列q中填充資料,當佇列q為空時,a和b需要阻塞,等到c填充,佇列不為空時再繼續執行。
執行緒a: 執行緒c:
pthread_mutex_lock(); pthread_mutex_lock();
while(q.empty()) q.push();
pthread_cond_wait(); pthread_cond_signal();
q.pop();//執行其他操作
pthread_mutex_unlock(); pthread_mutex_unlock();
執行緒b和a相同。
問題:可以看到多出了乙個while,最開始看到這種寫法的時候我也很疑惑,當執行緒c向q中填充資料後,對執行緒a進行喚醒,執行緒a為什麼還要判斷一次q中是否為空。
解答:加入while的原因是為了防止虛假喚醒,比如在上述的場景中,執行緒a判斷到q中為空,阻塞於pthread_cond_wait();然後c向佇列中填充了乙個資料,進行喚醒(此時,a已經喚醒了,但是還在等待獲取互斥鎖),c釋放互斥鎖,注意,如果此時執行緒b將互斥鎖獲取了(那麼a只能繼續等待獲取互斥鎖),將q中資料取走,釋放互斥鎖,此時a再獲取互斥鎖,對q進行操作(其實此時q是乙個空佇列),可能會引發錯誤,所以需要再次判斷是否滿足條件,才能向下執行。
互斥量的簡單封裝:
class
mutex
~mutex()
void
lock()
void
unlock()
pthread_mutex_t*
get(
)private
: pthread_mutex_t _mutex;};
class
mutexguard
//對類mutex的乙個raii封裝,在退出臨界區時可以自動解鎖。
~mutexguard()
private
: mutex& _mutex;
//注意,持有引用
};
條件變數封裝class
condition
~condition()
void
wait()
void
waitforseconds
(int seconds)
void
notify()
void
notifyall()
private
: mutex& _mutex;
pthread_cond_t _con;
};
呼叫上述類實現多執行緒順序列印static int a =0;
mutex mutex;
condition cond
(mutex)
;void
*conditiontest2
(void
* arg)
}int main()
執行結果如下:
成功實現多執行緒順序列印
多執行緒按順序列印
於leetcode1114 我們提供了乙個類 public class foo public void second public void third 三個不同的執行緒將會共用乙個 foo 例項。請設計修改程式,以確保 second 方法在 first 方法之後被執行,third 方法在 seco...
多執行緒程式設計 3順序列印
主要功能 printa printl printi 定義三個訊號量依次給三個程序上鎖,並且通過其他程序來給自己解鎖 include include include include semaphore.h using namespace std 三個訊號量相當於三個鎖,可以等價替換 semaphore...
學習 多執行緒順序列印ABC
深夜睡不著,更新一波,突然想起了兩個月之前的一次面試的懊悔。為什麼懊悔呢,因為面試官出的題很簡單很簡單,但是我想太多了,導致出糗了。題目,有三個執行緒,按順序列印123。public class test1 extends thread thread b newthread new runnable...