[英]C# Is locking within getters and setters necessary?
當多個線程通過get / set-functions訪問屬性/字段時,是否有必要鎖定getter和setter。
例如:
你有一個定期獲取對象值的計時器。 並且定期更新此值的過程。
public class Task : IProgressProvider
{
...
public Progress GetProgress()
{
// should i lock here?
return _progress;
}
public SetProgress(Progress progress)
{
// and lock here?
_progress = progress;
}
public void Execute()
{
// ...
SetProgress(new Progress(10));
// ...
SetProgress(new Progress(50));
// ...
SetProgress(new Progress(100));
}
}
public class ProgressReporter
{
public ProgressReporter()
{
_timer = new Timer();
_timer.Elapsed += Elapsed;
// ...
}
// ...
public void Elapsed(...)
{
var progress = _task.GetProgress();
// ...
}
}
問題再次出現:
我認為變量_progress的設置和讀取是原子操作。 GetProgress獲取當前值非常重要。 如果不是這次,它將在下次調用GetProgress時獲得它。
如果_progress
是引用類型,那么讀取和寫入其值(即更改引用)確實是一個原子操作,因此您不需要鎖定示例中的特定代碼。 但是如果您在setter或getter中更改了多個字段,或者該字段不是具有原子讀/寫的類型(例如double),那么您需要鎖定。
如果您希望多個線程在任何時刻觀察到相同的值,那么您確實需要額外的鎖定。 如果你不在乎一個線程是否可以讀取一個值而另一個線程是另一個值(因為它在中間發生了變化),那么鎖定似乎並不是必需的。
我肯定會讓它變得不穩定。 volatile
正是出於這個目的。 它至少會阻止優化編譯器在不應該的情況下緩存該值(如果它會做這樣的事情)。
總結一下:對於您的必需用法,您應該使_progress
易變,但您不需要鎖定對它的訪問權限。
根據您的方法簽名,您似乎只有一個線程將更新狀態。 如果是這樣,您在SetProgress上不需要任何鎖定。
如果你有多個線程更新Progress變量,你仍然不需要在set函數中有一個鎖,因為它是原子的(假設它是一個引用變量,它看起來像)。
但是,如果您讀取該值然后向其中添加一個數字(例如,獲取當前進度並將其增加10)那么您將需要鎖定該調用。 如果這是你的意圖,因為你的調用對象不應該真正負責這個對象的完整性,我建議創建一個處理更新的方法。
public Progress IncrProgress(int incr)
{
lock (_progressLock)
{
// Get the current progress
int current = _progress.GetPercentage();
current += incr;
_progress = new Progress(current);
}
return _progress;
}
至於你的另一個問題,_progress應該被標記為volatile,因為它被另一個線程更新。
http://msdn.microsoft.com/en-us/library/x13ttww7(v=vs.71).aspx
如果沒有人會改變對象的狀態,我認為屬性/字段只是重要的。
因此,如果您只有一個線程設置進度而其他人正在讀取它 - 則不需要鎖定。
即使是這種情況,使Progress對象不可變也是一個不錯的設計 - 因為你在設計中明確說明:為什么你不需要任何鎖定? 因為對象無法改變。
此外,如果稍后有更改/重新設計(決定更改/改變進度對象),則不會意外地引入線程錯誤。
not be necessary? 甚至volatile關鍵字沒有必要? 既然你不在乎你是否輸了一兩個進度。 該關鍵字全部與字段“序列化訪問”有關。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.