確保真正的執行緒安全 微軟為什麼不提供執行緒安全庫

2021-05-27 00:52:56 字數 3029 閱讀 3279

執行緒安全在高併發情況下是乙個非常嚴重的問題。以下**在多執行緒訪問時,會出現問題。我們以list.add為例,來說明在多執行緒訪問下的狀況。以下**是list.add的實現。

public

void

add(t item)

當兩個執行緒同時訪問乙個list的add方法時,這個方法的第一條指令就可能出現不一致性了。因為,此時兩個執行緒訪問時_size都是一樣的,正確情況下list應該執行ensurecapacity(this._size + 2)而不再是ensurecapacity(this._size + 1)了。為了確保list的執行緒安全,我們必須保證在任意時刻只能有乙個執行緒來更改list的資料。這樣我們的******threadsafelist就誕生了,在這個list中我們對其的每乙個讀寫操作都加上乙個排它鎖。

public

class

******threadsafelist

<

t>

: ilist

<

t>

public

void

removeat(

intindex)

}public

void

add(t item)}//

others......}

這樣我們確保list的資料在任意時刻都只有乙個執行緒對其進行訪問,看起來安全很多了。不過,如果所謂執行緒安全是這麼簡單的話,微軟為什麼不提供乙個threadsafelist呢?jaredpar msft在這篇文章中做了乙個描述《why are thread safe collections so hard?他解釋微軟不實現執行緒安全集合類,是因為以上這樣的所謂「執行緒安全」並不是真正安全,我們可以從以下的**中看出端倪。

var list 

=new

******threadsafelist

<

int>

();//

ohters …

if(list.count 

>0)

在以上**中,我們建立了******threadsafelist這個類,其中有乙個**段就是用於獲取list的預設值。如果有多個執行緒訪問這段**時,依然會出現資料不一致問題。即在執行「return list[0]」這條語句是,它是以「list.count > 0」為前提的,當兩個執行緒同時對這個******threadsafelist操作時,當前執行緒訪問到list.count大於0,但之後可能另乙個執行緒將list清空了,這時候當前執行緒再來返回list[0]時就會出現indexoutofrangeexception了。******threadsafelist保證了list內部資料只能由乙個執行緒來操作,但是對於上面的**,它是無法保證資料不一致的。******threadsafelist僅能夠被稱為「資料線程安全」,這也是微軟不提供執行緒安全集合類的原因了。jaredpar msft提出了乙個真正解決執行緒安全的方法。那就是將******threadsafelist的_syncroot 暴露出來。

public

class

******threadsafelist

<

t>

: ilist

<

t>}//

others……}

使用list時,需要使用到syncroot來加鎖。 

lock

(list.syncroot)}

不過,使用這種方式,有幾個缺陷。第一,沒有乙個良好的guide來指導編寫執行緒安全的**;第二,當syncroot使用範圍過大時,非常容易造成死鎖。下面是一段可能產生死鎖的**。 

var list1 

=new

******threadsafelist

<

int>

();var list2 

=new

******threadsafelist

<

int>

();new

thread(()

=>

}).start();

newthread(()

=>

}).start();

對於死鎖這個問題,我們採取的方法是使用monitor.tryenter,從而來避免一直死鎖。對於前乙個問題,我這邊僅是基於disposablelocker來實現盡可能的執行緒安全,對於如何使用,我目前依然沒有乙個良好的理論,只能說我們在設計高併發的api時,對多個執行緒可以同時訪問的物件都需要加以判斷從而來確定需要採用什麼樣的方式處理。

namespace

uishell.osgi.collection

private

void

logwhenacquirelockfailed()

milliseconds.", 

_millisecondstimeout

));}

}public

void

dispose()

private

void

dispose(

bool

disposing)}}

~disposablelocker()}}

當出現死鎖時,這裡採用的解決方案是記錄日誌,然後繼續執行。不過,這種方法有一定缺陷,可能在極端情況下引起資料不一致。因此,我們可能需要丟擲異常或者讓鎖一直持續下去。******threadsafelist此時將以以下方式來實現。

public

class

******threadsafelist

<

t>

: ilist

<

t>

}public

intmillisecondstimeoutonlock

public

disposablelocker createlocker()

public

void

add(t item)}//

others……}

接下來使用list.createlocker來建立乙個全域性鎖

using

(list.createlocker())}

大家可以看一下是否還有更好的方式來保證執行緒絕對安全。

為什麼HashMap不是執行緒安全的

序 最近因為專案的需求,經常會面試一些新人,也就會問他們一些基本的問題,例如,hashmap和hashtable的區別是什麼,一般人想到的就是hashmap不是執行緒安全,這點我想幾乎來面試的人都知道,但是再深入問下為什麼hashmap不是執行緒安全的,幾乎沒有人答上來,當然了,我也不會因為你回答不...

HashMap為什麼是執行緒不安全的

hashmap底層是乙個entry陣列,當發生hash衝突的時候,hashmap是採用鍊錶的方式來解決的,在對應的陣列位置存放鍊錶的頭結點。對鍊錶而言,新加入的節點會從頭結點加入。我們來分析一下多執行緒訪問 1.在hashmap做put操作的時候會呼叫下面方法 新增entry。將 key value...

ArrayList為什麼是執行緒不安全的

提到執行緒安全我們應該第一時間想到鎖機制,當乙個執行緒訪問該類的某個資料時,進行保護,其他執行緒不能進行訪問直到該執行緒讀取完,其他執行緒才可使用,所以通過加鎖我們就可以保證乙個執行緒的安全性,list介面下面有兩個實現,乙個是arraylist,另外乙個是vector。從原始碼的角度來看,因為ve...