[英]How can I write a conditional lock in C#?
我認為這個問題叫“競爭條件!”。 如果條件在檢查后不久、但在線程進入代碼的臨界區之前從真變為假怎么辦? 或者當一個線程正在執行它的過程中?
我不是線程專家,但聽起來您可能正在尋找這樣的東西(雙重檢查鎖定)。 這個想法是在獲取鎖之前和之后檢查條件。
private static object lockHolder = new object();
if (ActionIsValid()) {
lock(lockHolder) {
if (ActionIsValid()) {
DoSomething();
}
}
}
Action doThatThing = someMethod;
if (condition)
{
lock(thatThing)
{
doThatThing();
}
}
else
{
doThatThing();
}
bool locked = false;
if (condition) {
Monitor.Enter(lockObject);
locked = true;
}
try {
// possibly critical section
}
finally {
if (locked) Monitor.Exit(lockObject);
}
編輯:是的,除非您可以確保在線程進入時條件保持不變,否則存在競爭條件。
實際上,為了避免競爭條件,我很想在這里使用ReaderWriterLockSlim
- 將並發訪問視為讀鎖,將獨占訪問視為寫鎖。 這樣,如果條件發生變化,您最終不會在該區域中盲目執行一些不適當的代碼(假設它是安全的); 有點冗長,但是(格式化為空格):
if (someCondition) {
lockObj.EnterReadLock();
try { Foo(); }
finally { lockObj.ExitReadLock(); }
} else {
lockObj.EnterWriteLock();
try { Foo(); }
finally { lockObj.ExitWriteLock(); }
}
如上所述,使用雙重檢查鎖定模式。 這就是海事組織的訣竅:)
確保您的鎖對象為static ,如 not.that.dave.foley.myopenid.com 的示例中所列。
如果您有許多需要條件鎖定的方法/屬性,您不想一遍又一遍地重復相同的模式。 我提出以下技巧:
使用實現IDisposable
的私有幫助器struct
我們可以封裝條件/鎖而無需可測量的開銷。
public void DoStuff()
{
using (ConditionalLock())
{
// Thread-safe code
}
}
這很容易實現。 這是演示此模式的示例類:
public class Counter
{
private static readonly int MAX_COUNT = 100;
private readonly bool synchronized;
private int count;
private readonly object lockObject = new object();
private int lockCount;
public Counter(bool synchronized)
{
this.synchronized = synchronized;
}
public int Count
{
get
{
using (ConditionalLock())
{
return count;
}
}
}
public int LockCount
{
get
{
using (ConditionalLock())
{
return lockCount;
}
}
}
public void Increase()
{
using (ConditionalLock())
{
if (count < MAX_COUNT)
{
Thread.Sleep(10);
++count;
}
}
}
private LockHelper ConditionalLock() => new LockHelper(this);
// This is where the magic happens!
private readonly struct LockHelper : IDisposable
{
private readonly Counter counter;
private readonly bool lockTaken;
public LockHelper(Counter counter)
{
this.counter = counter;
lockTaken = false;
if (counter.synchronized)
{
Monitor.Enter(counter.lockObject, ref lockTaken);
counter.lockCount++;
}
}
private void Exit()
{
if (lockTaken)
{
Monitor.Exit(counter.lockObject);
}
}
void IDisposable.Dispose() => Exit();
}
}
現在,讓我們創建一個小示例程序來演示其正確性。
class Program
{
static void Main(string[] args)
{
var onlyOnThisThread = new Counter(synchronized: false);
IncreaseToMax(c1);
var onManyThreads = new Counter(synchronized: true);
var t1 = Task.Factory.StartNew(() => IncreaseToMax(c2));
var t2 = Task.Factory.StartNew(() => IncreaseToMax(c2));
var t3 = Task.Factory.StartNew(() => IncreaseToMax(c2));
Task.WaitAll(t1, t2, t3);
Console.WriteLine($"Counter(false) => Count = {c1.Count}, LockCount = {c1.LockCount}");
Console.WriteLine($"Counter(true) => Count = {c2.Count}, LockCount = {c2.LockCount}");
}
private static void IncreaseToMax(Counter counter)
{
for (int i = 0; i < 1000; i++)
{
counter.Increase();
}
}
}
Counter(false) => Count = 100, LockCount = 0
Counter(true) => Count = 100, LockCount = 3002
現在您可以讓調用者決定是否需要鎖定(代價高昂)。
我猜你有一些看起來像這樣的代碼:
private Monkey GetScaryMonkey(int numberOfHeads){
Monkey ape = null;
lock(this) {
ape = new Monkey();
ape.AddHeads(numberOfHeads);
}
return ape;
}
為了使這個有條件,你不能這樣做:
private Monkey GetScaryMonkey(int numberOfHeads){
if ( numberOfHeads > 1 ) {
lock(this) {
return CreateNewMonkey( numberOfHeads );
}
}
return CreateNewMonkey( numberOfHeads );
}
應該工作,不是嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.