C#不允許鎖定空值。我想我可以在鎖定之前檢查該值是否爲null,但是因爲我沒有鎖定它,另一個線程可能會出現並將值設爲null!我怎樣才能避免這種競爭狀態?爲什麼C#不允許空值被鎖定?
回答
鎖定一個永不爲空的值,例如
Object _lockOnMe = new Object();
Object _iMightBeNull;
public void DoSomeKungFu() {
if (_iMightBeNull == null) {
lock (_lockOnMe) {
if (_iMightBeNull == null) {
_iMightBeNull = ... whatever ...;
}
}
}
}
另外要注意避免雙重檢查鎖定這個有趣的比賽條件:Memory Model Guarantees in Double-checked Locking
可能很高興將只讀添加到鎖對象以調用所需的不變性 – cordialgerm
爲什麼鎖_lockOnMe可以阻止其他人訪問_iMightBeNull? –
-1:你的代碼仍然容易受到你鏈接到的競爭條件的影響; '_iMightBeNull'需要聲明爲volatile。或者,最好是隻使用'懶惰的
這裏有兩個問題:
首先,不要在null
對象鎖定。它沒有任何意義,因爲兩個對象null
如何區分?
二,安全初始化在多線程環境變量,可以使用雙重檢查鎖定模式:
if (o == null) {
lock (lockObj) {
if (o == null) {
o = new Object();
}
}
}
這將確保另一個線程尚未初始化的對象,可用於實現單身模式。
不能對空值鎖定,因爲CLR沒有地方的同步塊重視,這是允許的CLR同步通過Monitor.Enter /退出鍵訪問任意對象(這是什麼lock
內部使用)
+1:這是最正確的答案。 –
爲什麼C#不允許空值被鎖定?
Paul's answer是迄今爲止唯一技術上正確的,所以我會接受那一個。這是因爲.NET中的監視器使用附加到所有參考類型的同步塊。如果你有一個變量null
那麼它不是指任何對象,這意味着監視器無法訪問可用的同步塊。
我該如何避免這種競爭狀態?
傳統的方法是鎖定一個對象引用,你知道永遠不會是null
。如果您發現自己處於無法得到保證的情況,那麼我會將其歸類爲非傳統方法。除非您更詳細地描述可導致可空鎖定目標的特定情況,否則實際上我沒有太多可以提及的地方。
您的問題的第一部分已經回答了,但我想爲您的問題的第二部分添加一些內容。
使用不同的對象來執行鎖定更爲簡單,特別是在這種情況下。 這也解決了維護關鍵部分中多個共享對象的狀態的問題,例如,員工名單和員工照片列表。
此外,這種技術也是有用的,當你必須獲得對原始類型的鎖如int或十進制等
在我看來,如果你使用這個技術,因爲其他人建議,那麼你並不需要執行空檢查兩次。例如在接受的答案克里斯已經使用,如果條件兩次真的沒有任何區別,因爲鎖定的對象是不同的,那麼實際上正在修改,如果你鎖定在一個不同的對象然後執行第一個空檢查是無用的和浪費CPU 。
我會建議下面的一段代碼;
object readonly syncRootEmployee = new object();
List<Employee> employeeList = null;
List<EmployeePhoto> employeePhotoList = null;
public void AddEmployee(Employee employee, List<EmployeePhoto> photos)
{
lock (syncRootEmployee)
{
if (employeeList == null)
{
employeeList = new List<Employee>();
}
if (employeePhotoList == null)
{
employeePhotoList = new List<EmployeePhoto>();
}
employeeList.Add(employee);
foreach(EmployeePhoto ep in photos)
{
employeePhotoList.Add(ep);
}
}
}
我在這裏看不到任何競賽狀況,如果有人看到競賽狀況請在評論中回覆。正如你在上面的代碼中看到的那樣,它立即解決了3個問題,一個在鎖定之前不需要空的檢查,其次它在不鎖定兩個共享源的情況下創建關鍵部分,並且第三個鎖定多個對象由於寫入時缺乏注意力而導致死鎖碼。
以下是我如何使用原始類型上的鎖。
object readonly syncRootIteration = new object();
long iterationCount = 0;
long iterationTimeMs = 0;
public void IncrementIterationCount(long timeTook)
{
lock (syncRootIteration)
{
iterationCount++;
iterationTimeMs = timeTook;
}
}
public long GetIterationAvgTimeMs()
{
long result = 0;
//if read without lock the result might not be accurate
lock (syncRootIteration)
{
if (this.iterationCount > 0)
{
result = this.iterationTimeMs/this.iterationCount;
}
}
return result;
}
快樂線程:)
- 1. 爲什麼不允許鎖定(<integer var>),但允許Monitor.Enter(<integer var>)?
- 2. %爲什麼不允許
- 3. 爲什麼Hashtable不允許空鍵或值?
- 4. 爲什麼不允許構造函數被重新定義?
- 5. 此修改是不允許的,因爲選擇被鎖定
- 6. 爲什麼在C#在這個成員的初始值是不允許的,但在VB.Net我被允許
- 7. 爲什麼允許空的wchar_t文字?
- 8. 爲什麼C++不允許這個默認值?
- 9. 嵌套函數不被允許,但爲什麼嵌套函數原型被允許? [C++]
- 10. 爲什麼C++不允許用戶定義的運算符?
- 11. 列不允許有空值
- 12. 我如何使不允許列允許空和允許空列不允許空
- 13. C - 爲什麼我允許這樣做?
- 14. 爲什麼C#允許重寫異步?
- 15. 爲什麼Spring不允許對讀操作進行獨佔鎖定?
- 16. Joi:「tel」不允許爲空
- 17. 爲什麼繼承成員不被允許?
- 18. 爲什麼這個默認模板參數不被允許?
- 19. 爲什麼我的TableLayout不允許被誇大?
- 20. 鎖定用戶,不允許mysql更新
- 21. 爲什麼我在RStudio中獲得「命名空間被鎖定」?
- 22. 爲什麼不允許密碼驗證?
- 23. 爲什麼sfinae如果constexpr不允許?
- 24. 爲什麼樣式表列不允許?
- 25. 爲什麼a,b = 1不允許?
- 26. 爲什麼PHP不允許比較?
- 27. 爲什麼CalendarProvider不允許寫入ExtendedProperties?
- 28. 爲什麼泛型在「?extends Klass」被允許時被認爲是不變的?
- 29. 爲什麼C#允許一個無效的枚舉值
- 30. 允許jTextField爲空?
你爲什麼不只是使用靜態初始化的一員,*始終不爲空* – zerkms
據我瞭解,空本質上不過。你怎麼能鎖上什麼?換句話說,string myString = null聲明瞭一個字符串類型的變量,但這就是它的全部內容 - 它不作爲一個對象存在,因爲它沒有任何價值。 – Tim