【VS开发】C++线程安全
我們是多么渴望各種C++類都是多線程安全的,然而一旦涉及到對象間的交互,這樣的渴望可能就只能是奢望了。下面,我們以設計一個雙向鏈結點為例,看看要使其多線程安全將會帶來一些什么問題。
class?DoublyLinedNode{
???????DoublyLinedNode*?pPrevNode_;
???????DoublyLinedNode*?pNextNode_;
?
public:
???????DoublyLinedNode() :?pPrevNode_(0),?pNextNode_(0){}
???????virtual?~DoublyLinedNode();
?
public:
???????const?DoublyLinedNode*?GetPrevNode()?const{return?pPrevNode_;}
???????const?DoublyLinedNode*?GetNextNode()?const{return?pNextNode_;}
?
public:
???????void?InsertPrevNode(DoublyLinedNode*?p);
???????void?InsertNextNode(DoublyLinedNode*?p);
???????void?Break();
};
這是一個簡單的雙向鏈結點類,我們就討論討論其Break接口,這個接口的作用是使結點從其所在的鏈中斷開,如圖:
?
它的實現可能是這樣的:
void?DoublyLinedNode::Break()
{
???????if?(pPrevNode_)
???????{
??????????????pPrevNode_->pNextNode_?=?pNextNode_;
???????}
???????if?(pNextNode_)
???????{
??????????????pNextNode_->pPrevNode_?=?pPrevNode_;
???????}
???????pPrevNode_?= 0;
???????pNextNode_?= 0;
}
這個實現是單線程模式的,沒有多線程安全性。
第一次嘗試:
void?DoublyLinedNode::Break()
{
Lock();
???????if?(pPrevNode_)
???????{
??????????????pPrevNode_->pNextNode_?=?pNextNode_;
???????}
???????if?(pNextNode_)
???????{
??????????????pNextNode_->pPrevNode_?=?pPrevNode_;
???????}
???????pPrevNode_?= 0;
???????pNextNode_?= 0;
???????UnLock();
}
我們第一次嘗試將這個接口的代碼用多線程鎖鎖住了,然而問題很明顯:
if?(pPrevNode_)
{
???????pPrevNode_->pNextNode_?=?pNextNode_;
}
if?(pNextNode_)
{
???????pNextNode_->pPrevNode_?=?pPrevNode_;
}
我們這兩個對前向和后向結點的操作是修改另外兩個對象的內部狀態,多線程中,可能在此時正好有其他線程在對這兩個對象進行操作(訪問),或許程序就會因此而崩潰。
第二次嘗試:
void?DoublyLinedNode::Break()
{
Lock();
???????if?(pPrevNode_)
???????{
??????????????pPrevNode_->SetNextNode(pNextNode_);?// SetNextNode同樣添加了鎖保護
???????}
???????if?(pNextNode_)
???????{
??????????????pNextNode_->SetPrevNode(pPrevNode_);?// SetPrevNode同樣添加了鎖保護
???????}
???????pPrevNode_?= 0;
???????pNextNode_?= 0;
???????UnLock();
}
這第二次嘗試將我們對前向和后繼結點的內部狀態的直接修改改成了對其接口的調用,我們試圖通過在其各種接口中加鎖來達到多線程安全的目的。然而這卻引入了新的問題,我們在一個被鎖住的代碼中進行了又調用了另外會使用鎖的代碼,這最可能引發的問題就是資源競爭,而在我們這次嘗試中引如的問題的確就是資源競爭,導致死鎖:
?
我們在不同線程中對結點1和結點2同時調用Break,當1申請到自身的鎖之后,準備調用2的接口,此時2也申請到了自身的鎖,準備調用1的接口。由于1已經占有了自身的鎖,2也占有了自身的鎖,那么1將會在調用2的接口的地方等待2的鎖,而2將會在調用1的接口的地方等待1,?1和2的相互等待就形成了死鎖。
第三次嘗試:
void?DoublyLinedNode::Break()
{
Lock();
???????if?(pPrevNode_)
???????{
pPrevNode_->?Lock();
??????????????pPrevNode_->SetNextNode(pNextNode_);
pPrevNode_->?UnLock?();
???????}
???????if?(pNextNode_)
???????{
pNextNode_->?Lock();
??????????????pNextNode_->SetPrevNode(pPrevNode_);
pNextNode_->?UnLock?();
???????}
???????pPrevNode_?= 0;
???????pNextNode_?= 0;
???????UnLock();
}
這次嘗試顯得比較愚蠢,將外部對象加鎖的過程提到了自身Break當中效果和第二次嘗試是一樣的,沒有得到任何的改善。
第四次嘗試:
void?DoublyLinedNode::Break()
{
SharedLock();
???????if?(pPrevNode_)
???????{
??????????????pPrevNode_->SetNextNode(pNextNode_);
???????}
???????if?(pNextNode_)
???????{
??????????????pNextNode_->SetPrevNode(pPrevNode_);
???????}
???????pPrevNode_?= 0;
???????pNextNode_?= 0;
???????SharedUnLock();
}
這次嘗試取得了一定的成功,對于這些關系密切,存在相互調用的對象,我們使用了共享鎖,它的確將我們的多線程訪問沖突和死鎖問題解決了,但是這個共享鎖的實現難度是相當大的,你必須要保證可能產生相互調用的對象都要進行鎖共享,那么你對于增加、修改、刪除對象這些管理工作將會變得極度困難,稍有差池就會引發問題,而且別人在使用你的類的時候也同樣需要處處小心,這不是我們所期望的。
?
以上我們進行了四次嘗試將我們的雙向鏈結點類設計成多線程安全,顯然我們已經筋疲力盡,卻未能達到滿意的效果。
在這里我建議大家設計這種類的時候盡量設計成單線程模式,在框架設計中去考慮多線程問題,比如使用單線程訪問對象,而模塊間使用異步通信來進行交互等。
?
多線程編程的確非常困難,C++在這方面又表現得力不從心,我在這里引入這個問題旨在于告誡大家在對待多線程問題上一定要細心細心再細心。
轉載于:https://www.cnblogs.com/huty/p/8518634.html
總結
以上是生活随笔為你收集整理的【VS开发】C++线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode 之Median of
- 下一篇: 2011年日本大地震哪些国家参与救援20