大的項(xiàng)目分解成一個(gè)個(gè)小的模塊。
單例設(shè)計(jì)模式(高頻使用)整個(gè)項(xiàng)目中,有某個(gè)或者某些特殊的類,屬于類的對(duì)象,只能創(chuàng)建一個(gè),多的創(chuàng)建不了。
// 單例模式示范代碼
class MyCas
{private:
MyCas() {} //私有化構(gòu)造函數(shù),避免該類創(chuàng)建多個(gè)
private:
static MyCas* m_instance;
static mutex m_mutex;
public:
static MyCas* GetInstance()
{if (m_instance == NULL) //雙重鎖定,雙重檢查,保證只有當(dāng)m_instance不為空的時(shí)候才加鎖,提高效率
{unique_lockmymutex(m_mutex);
if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
}
}
return m_instance;
}
class CCarhuishouMyCas //類中套類來釋放new出來的對(duì)象
{public:
~CCarhuishouMyCas()
{if (MyCas::m_instance)
{delete MyCas::m_instance;
MyCas::m_instance = NULL;
}
}
};
};
MyCas* MyCas::m_instance = NULL;
mutex MyCas::m_mutex;
int main()
{MyCas* p_a = MyCas::GetInstance();
return 0;
}
單例模式共享數(shù)據(jù)問題分析解決解決若數(shù)據(jù)是只讀不寫的,就不需要加鎖保護(hù)
面臨的問題:需要多個(gè)線程中去創(chuàng)建單例類,這時(shí)有可能會(huì)出現(xiàn)GetInstance()這種成員函數(shù)要互斥
解決方法:就是要改進(jìn)getinstance函數(shù)
//改進(jìn)前
static MyCas* GetInstance()
{if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
}
return m_instance;
}
//改進(jìn)后
static MyCas* GetInstance()
{if (m_instance == NULL) //雙重鎖定,雙重檢查,保證只有當(dāng)m_instance不為空的時(shí)候才加鎖,提高效率
{unique_lockmymutex(m_mutex);
if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
}
}
return m_instance;
}
std::call_once()c++11引入的函數(shù),該函數(shù)的第二個(gè)參數(shù)是一個(gè)函數(shù)名a();
call_once()功能可以保證函數(shù)a()只被調(diào)用一次
call_once()具備互斥量的能力,并且效率上比互斥量消耗的資源少。
它主要是通過綁定一個(gè)std::one_flag結(jié)構(gòu)(標(biāo)志位實(shí)現(xiàn)的)。
下面示范通過callz_once()改造單例模式,替代其互斥量。
// 單例模式示范代碼
class MyCas
{private:
MyCas() {} //私有化構(gòu)造函數(shù),避免該類創(chuàng)建多個(gè)
private:
static MyCas* m_instance;
static once_flag g_flag;
public:
static void CreateInstance()//這里表示只被調(diào)用一次的函數(shù)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序結(jié)束,以至于當(dāng)這個(gè)對(duì)象釋放時(shí)調(diào)用析構(gòu)函數(shù)釋放new出來的對(duì)象
}
static MyCas* GetInstance()
{call_once(g_flag, CreateInstance);
return m_instance;
}
class CCarhuishouMyCas //類中套類來釋放new出來的對(duì)象
{public:
~CCarhuishouMyCas()
{if (MyCas::m_instance)
{delete MyCas::m_instance;
MyCas::m_instance = NULL;
}
}
};
};
MyCas* MyCas::m_instance = NULL;
once_flag MyCas:: g_flag;
int main()
{MyCas* p_a = MyCas::GetInstance();
return 0;
}
std::condition_variable ,wait(),notify_one,notify_all();std::condition_variable
實(shí)際上是一個(gè)類,是需要和互斥量mutex配合使用,使用的時(shí)候要生成這個(gè)類
wait()
用來等待一個(gè)東西,當(dāng)?shù)诙€(gè)參數(shù)返回值是false時(shí)候,wait()將解鎖互斥量,并且堵塞到本行;堵塞到其他線程調(diào)用notify_one()成員函數(shù)為止如果沒有第二個(gè)參數(shù),那默認(rèn)效果和第二個(gè)參數(shù)為false一樣,立即堵塞。
當(dāng)其他線程調(diào)用notify_one()將wait喚醒后,wait就解除阻塞狀態(tài)。然后wait會(huì)不斷嘗試重新獲取互斥量的鎖,如果獲取不到wait會(huì)卡在這里等待鎖的獲取,當(dāng)獲取到鎖后,
如果wait第二個(gè)參數(shù)是lamda表達(dá)式,會(huì)繼續(xù)執(zhí)行l(wèi)amda表達(dá)式,此時(shí)如果為true,繼續(xù)執(zhí)行(此時(shí)還是加鎖狀態(tài)
如果此時(shí)沒有第二個(gè)參數(shù),則默認(rèn)是true;往下繼續(xù)執(zhí)行。一般來說wait()需要第二個(gè)參數(shù),防止虛假喚醒
notify_all() 則是喚醒全部wait(),在下面示例代碼中,其實(shí)notify_one 和notify_all效果是一樣的。
#include#include#include#include#includeusing namespace std;
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 10000; i++)
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<guard1(my_mutex);
msgRecvQueue.push_back(i);
my_cond.notify_one();//嘗試把wait的線程喚醒,不一定能喚醒,如果線程此時(shí)沒有卡在wait哪里的話
}
}
void outMsgRecvQueue()
{while(1)
{unique_lockguard1(my_mutex);
my_cond.wait(guard1, [this] {if (!msgRecvQueue.empty())
return true;
else
return false;
});
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
guard1.unlock(); //因?yàn)橛玫氖穷惸0宕藭r(shí)還是鎖著的,可以手動(dòng)解鎖,避免卡太長時(shí)間
//......一些操作
}
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
async future packaged_task promise一 std::async
async是一個(gè)類模板,用于啟動(dòng)一個(gè)異步任務(wù)(即開啟一個(gè)線程來執(zhí)行對(duì)應(yīng)的線程入口函數(shù)),然后會(huì)返回一個(gè)futrue對(duì)象(里面包括了該線程返回的結(jié)果,可以通過futrue對(duì)象的成員函數(shù)get()來獲取結(jié)果) (future對(duì)象里面會(huì)保存一個(gè)值,在將來的某個(gè)時(shí)刻可以得到)
class A
{public:
int mythread(int k )
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
};
int mythread()
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int main()
{int tm = 12;
class A a;
cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
futureresult = std::async(&A::mythread,&a,tm);
cout<< ".........."<< endl;
cout<< result.get()<< endl; //如果線程沒執(zhí)行完,線程會(huì)卡在這里等待線程執(zhí)行完畢返回結(jié)果。
cout<< "!!!!!!"<< endl;
return 0;
}
此外,我們可以通過額外向async()傳遞一個(gè)參數(shù),該參數(shù)類型是launch類(枚舉類型)來達(dá)到一些效果
1,std::launch::deferred:表示線程如果函數(shù)調(diào)用延遲到std::future的wait()或者get()函數(shù)調(diào)用是才執(zhí)行,且如果不調(diào)用wait()和get(),該線程直接不執(zhí)行。即使調(diào)用wait()和get()也不創(chuàng)建新線程,都是主線程里面執(zhí)行線程入口函數(shù)。
futureresult = std::async(std::launch::deferred,&A::mythread,&a,tm);
2,std::launch::async:表示在調(diào)用async函數(shù)的時(shí)候就開始創(chuàng)建線程
futureresult = std::async(std::launch::async,&A::mythread,&a,tm);
二 std::packaged_task
packaged_task是一個(gè)類模板,模板參數(shù)是各種可調(diào)用對(duì)象,通過packaged_task包裝起來,方便將來作為線程入口函數(shù)來調(diào)用
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt(mythread);
thread t1(ref(mypt), 1); //ref()指用引用的方式傳遞
t1.join();
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//改為lamda表達(dá)式
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
thread t1(ref(mypt), 1); //ref()指用引用的方式傳遞
t1.join();
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//也可以直接調(diào)用,但是就都在主線程中執(zhí)行了。
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
mypt(100);
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//和容器關(guān)聯(lián)packaged_task
vector>mytasks;
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
mytasks.push_back(std::move(mypt));//通過std::move,可以避免不必要的拷貝操作,是為性能而生,將對(duì)象的狀態(tài)或者所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象,只是轉(zhuǎn)移,沒有內(nèi)存的搬遷或者內(nèi)存拷貝。
packaged_taskmypt2;
auto it = mytasks.begin();
mypt2 = std::move(*it);
mytasks.erase(it); //刪除第一個(gè)元素,迭代器已經(jīng)失效了,不能再使用it;
mypt2(123);
futureresult = mypt2.get_future();
cout<< result.get()<< endl;
return 0;
}
二 std::promise
promise
可以能夠在某個(gè)線程中給它賦值,然后我們可以在其他線程中,把這個(gè)值給取出來
void mythread(promise& tmmp, int calc)
{calc++;
calc *= 10;
std::chrono::milliseconds dur(5000);
std::this_thread::sleep_for(dur);
int result = calc;
tmmp.set_value(result);
return;
}
void mythread2(futuretmmp)
{auto k = tmmp.get();
cout<< "thread2 "<< k<< endl;
return;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
promisemyprom;
thread t1(mythread, ref(myprom), 12);
t1.join();
thread t2(mythread2,myprom.get_future());
t2.join();
return 0;
}
future其他成員函數(shù),shared_future atomicfuture_status 有三種狀態(tài):timeout ready deferred
class A
{public:
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
};
int main()
{int tm = 12;
class A a;
cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
futureresult = std::async( &A::mythread, &a, tm);
//futureresult = std::async(std::launch::deferred, &A::mythread, &a,tm);
cout<< ".........."<< endl;
// cout<< result.get()<< endl; //如果線程沒執(zhí)行完,線程會(huì)卡在這里等待線程執(zhí)行完畢返回結(jié)果。
future_status status = result.wait_for(std::chrono::seconds(6));
if (status == future_status::timeout) //超時(shí),指的的等待時(shí)間結(jié)束后,線程還沒有運(yùn)行完
{cout<< "超時(shí),線程還沒有執(zhí)行完"<< endl;
}
else if (status == future_status::ready) // 準(zhǔn)備好,表示等待時(shí)間結(jié)束后,線程已經(jīng)運(yùn)行完了
{cout<< "線程已經(jīng)執(zhí)行完畢"<< endl;
}
else if (status == future_status::deferred) //如果async第一個(gè)參數(shù)是deferred,該條件成立
{cout<< "線程被延遲執(zhí)行"<< endl;
cout<< result.get()<< endl;
}
cout<< "!!!!!!"<< endl;
return 0;
}
std::shared_future (主要是為了解決future_get()只能被調(diào)用一次的問題(這里的get其實(shí)這里是轉(zhuǎn)移的意思),適合用于多個(gè)線程使用同一個(gè)future,本質(zhì)就是把get()的語義由轉(zhuǎn)移變?yōu)榭截?
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int mythread2(shared_future& g)
{cout<< "get_threadid"<< std::this_thread::get_id()<< endl;
cout<< g.get()<< endl;
return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt(mythread);
thread t1(ref(mypt), 1); //ref()指用引用的方式傳遞
t1.join();
std::futureresult = mypt.get_future();
std::shared_futureresult2(move(result));//或者 std::shared_futureresult2(result.share());
//或者直接std::shared_futureresult2(mypt.get_future());
if (result.valid()) //可用于判斷result是否可以被get,就是是否為空
{cout<< "result有效"<< endl;
}
thread t2(mythread2, ref(result2)); //ref()指用引用的方式傳遞
thread t3(mythread2, ref(result2)); //ref()指用引用的方式傳遞
thread t4(mythread2, ref(result2)); //ref()指用引用的方式傳遞
t2.join();
t3.join();
t4.join();
cout<< "!!!!!"<< endl;
return 0;
}
原子操作:: std::atomic(比加鎖效率高,頻繁加鎖影響效率)
原子操作可以理解為一種不需要互斥量加鎖(無鎖)技術(shù)的多線程并發(fā)編程方式
原子操作:在多線程中不會(huì)被打斷的程序執(zhí)行片段
原子操作和互斥量的使用場(chǎng)景不同(原子:即不可分割)
互斥量加鎖一般針對(duì)一個(gè)代碼段,而原子操作針對(duì)的一般都是一個(gè)變量,而不是一個(gè)代碼段
一般用來處理變量的簡單操作
注意:一般的atomic原子操作,針對(duì)++,–,+=,&=,|=,^=,-=是支持的。其他的可能不支持,遇到這種情況,可以寫一小段代碼進(jìn)行測(cè)試
原子操作不支持拷貝構(gòu)造函數(shù)
例如:
以下這些都是不被允許的
atomic<>atm=0;
atomicatm2=atm
autoatm3=atm
若實(shí)在想復(fù)制,可以調(diào)用load()函數(shù)
load():以原子的方式讀atomic的值
atomicatm2(atm.load())
autoatm3(atm.load())
store():以原子方式寫入內(nèi)容
atm2.store(12) //atm2=12
下面將用互斥量和鎖進(jìn)行一個(gè)對(duì)比,并結(jié)合c++時(shí)間戳來比較兩者之間的效率差距
//使用原子操作需要時(shí)間
#include#include#include#include#includeusing namespace std;
atomicm_count = 0;
void mythread()
{for (int i = 0; i< 20000000; i++)
{m_count++;
}
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
cout<<"累計(jì) 40000000所需時(shí)間:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
cout<< m_count<< endl;
return 0;
}
累計(jì) 40000000所需時(shí)間:1098ms
40000000
//使用互斥量需要時(shí)間
#include#include#include#include#includeusing namespace std;
int m_count = 0;
mutex m_gmutex;
void mythread()
{for (int i = 0; i< 20000000; i++)
{m_gmutex.lock();
m_count++;
m_gmutex.unlock();
}
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
cout<<"累計(jì) 40000000所需時(shí)間:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
cout<< m_count<< endl;
return 0;
}
累計(jì) 40000000所需時(shí)間:6408ms
40000000
綜合上述,在累計(jì)40000000操作后,原子操作比互斥操作快6倍左右,隨著次數(shù)越來越多,差距會(huì)越大。
深入談?wù)刟syncasync有兩個(gè)參數(shù)
std::launch::deferred【延遲調(diào)用】
std::launch::async【強(qiáng)制創(chuàng)建一個(gè)線程】
用thread就說明創(chuàng)建線程,對(duì)于async來說,一般不叫它創(chuàng)建線程(雖然效果是能夠創(chuàng)建線程),一般叫它為創(chuàng)建一個(gè)異步任務(wù)
async和thread最明顯的不同是,async有時(shí)候并不創(chuàng)建線程(用std::launch::deferred參數(shù)的時(shí)候)
(a)當(dāng)使用std::launch::deferred參數(shù)的時(shí)候,并不會(huì)創(chuàng)建新的線程,他會(huì)延遲到future對(duì)象調(diào)用.get()或.wait()的時(shí)候才執(zhí)行入口函數(shù),如果沒 有調(diào)用.get()或.wait(),甚至連入口函數(shù)都不執(zhí)行
(b)當(dāng)使用std::launch::async時(shí)候,會(huì)強(qiáng)制這個(gè)異步任務(wù)在新線程上面執(zhí)行,意味著,系統(tǒng)必須要?jiǎng)?chuàng)建出一個(gè)新線程來運(yùn)行入口函數(shù)
?當(dāng)使用std::launch::deferred|std::launch::async,系統(tǒng)會(huì)根據(jù)情況自己選擇一種執(zhí)行
(d)若不使用參數(shù),效果和std::launch::deferred|std::launch::async一樣的,系統(tǒng)會(huì)自行決定會(huì)同步調(diào)用還是異步調(diào)用
所謂的自行決定是異步(創(chuàng)建新線程)還是同步(不創(chuàng)建新線程)方式運(yùn)行
(1)async和thread區(qū)別
(a)使用thread方式創(chuàng)建線程,不容易拿到線程的返回值(要拿可能需要使用全局變量的方式)
使用async創(chuàng)建異步任務(wù),可以很輕松的通過future獲得線程的返回值
(b)用thread創(chuàng)建線程,如果系統(tǒng)資源緊張,有可能會(huì)創(chuàng)建線程失敗,容易導(dǎo)致程序崩潰。
使用async一般不會(huì)崩潰,因?yàn)槿绻到y(tǒng)資源緊張的時(shí)候,async這種不加額外參數(shù)的調(diào)用就不會(huì)創(chuàng)建新線程,而是后續(xù)調(diào)用result.get()來請(qǐng)求結(jié)果,判斷是同步還是異步執(zhí)行。
經(jīng)驗(yàn):一個(gè)程序里面,線程數(shù)量不宜超過100-200
(2)當(dāng)使用async,不用參數(shù)的時(shí)候那如果判斷是同步還是異步,需要使用future_status來判斷
future_status status = result.wait_for(std::chrono::seconds(0));
if (status == future_status::timeout) //超時(shí),指的的等待時(shí)間結(jié)束后,線程還沒有運(yùn)行完
{cout<< "超時(shí),線程還沒有執(zhí)行完"<< endl;
}
else if (status == future_status::ready) // 準(zhǔn)備好,表示等待時(shí)間結(jié)束后,線程已經(jīng)運(yùn)行完了
{cout<< "線程已經(jīng)執(zhí)行完畢"<< endl;
}
//上面兩種情況說明都是異步執(zhí)行,從async創(chuàng)建時(shí)候線程就執(zhí)行了
else if (status == future_status::deferred) //如果async第一個(gè)參數(shù)是deferred,該條件成立
{cout<< "線程被延遲執(zhí)行"<< endl;
cout<< result.get()<< endl; //如果是這種情況,只有當(dāng).get()是函數(shù)才開始跑,且在同一個(gè)線程里面執(zhí)行。
}
window臨界區(qū)#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_ //開關(guān)
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
EnterCriticalSection(&my_winsec);
msgRecvQueue.push_back(i);
LeaveCriticalSection(&my_winsec);
#else
unique_lockguard1(my_mutex);
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
EnterCriticalSection(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
LeaveCriticalSection(&my_winsec);
#else
unique_lockguard1(my_mutex);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(& my_winsec);//windows中的臨界區(qū)使用必須初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
在同一線程中,windows中可以多次進(jìn)入臨界區(qū)。但是調(diào)用幾次臨界區(qū),就需要調(diào)用出幾次臨界區(qū)。而在c++11中,不允許同一個(gè)線程lock一個(gè)互斥量多次,否則報(bào)異常。
自動(dòng)析構(gòu)技術(shù)下面把win臨界區(qū)封裝成一個(gè)類,本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_ //開關(guān)
//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
unique_lockguard1(my_mutex);
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutex);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
recursive_mutex遞歸的獨(dú)占互斥量主要是為了解決重復(fù)lock會(huì)報(bào)錯(cuò)的問題
std::mutex:獨(dú)占互斥量,自己lock了,別人lock不了
recursive_mutex:遞歸的獨(dú)占互斥量:允許同一個(gè)線程,同一個(gè)互斥量多次lock。
#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_ //開關(guān)
//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
unique_lockguard1(my_mutext);//這里相當(dāng)與lock了三次
testfun2();
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutext);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
condition_variable my_cond;
std::recursive_mutex my_mutext;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_
void testfun1()
{lock_guardsbguard(my_mutext);
//干一些事情
}
void testfun2()
{lock_guardsbguard(my_mutext);
//干一些事情
testfun1();
}
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
但是這種多次鎖效率肯定比mutex效率差,如果通過改代碼能改成只lock一次最好。
帶超時(shí)的互斥量std::timed_mutexstd::timed_mutex帶超時(shí)的獨(dú)占互斥量
有兩個(gè)參數(shù):
try_lock_for();等待一段時(shí)間,無論有沒有拿到鎖,程序都走下去
try_lock_until();參數(shù)是一個(gè)未來的時(shí)間點(diǎn),在這個(gè)未來的時(shí)間沒到的時(shí)間內(nèi),如果拿到鎖,就走下去,到時(shí)間沒拿到,也走下去。
try_lock_for()示例
#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_ //開關(guān)
//本類自動(dòng)釋放臨界區(qū),類似mutex的lock_guard
//這種叫做RAII類,即(Resource Acquisition is initialization) “資源獲取即初始化”,容器,智能指針都是raii類
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
chrono::milliseconds timeout(100);;
if (my_mutext.try_lock_for(timeout))//等待100ms來獲取鎖
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
msgRecvQueue.push_back(i);
my_mutext.unlock(); //拿到鎖用完了記得釋放
}
else
{//沒有拿到鎖休眠100ms
chrono::milliseconds dur(100);
std::this_thread::sleep_for(dur);
}
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutext);
chrono::milliseconds dur(1000);
std::this_thread::sleep_for(dur);
int k = msgRecvQueue.front();
cout<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的臨界區(qū)使用必須初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
timed_mutex my_mutext;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的臨界區(qū),非常類似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
try_lock_until()示例
上面替換即可。
chrono::milliseconds timeout(100);
if (my_mutext.try_lock_until(chrono::steady_clock::now()+timeout))//等待100ms來獲取鎖
{cout<< "inmsgRecvqueue執(zhí)行,插入一個(gè)元素"<< i<< endl;
msgRecvQueue.push_back(i);
my_mutext.unlock(); //拿到鎖用完了記得釋放
}
else
{//沒有拿到鎖休眠100ms
chrono::milliseconds dur(100);
std::this_thread::sleep_for(dur);
}
recursive_mutex也有一個(gè)類似的std::recursive_timed_mutex
可以多次加鎖。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧