執行緒同步
當乙個執行緒可以修改的變數,其他執行緒也可以讀取或者修改的時候,我們就需要對這些執行緒進行同步,確保它們訪問變數的儲存內容時不會訪問到無效的值。
互斥量可以使用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...