我需要一种机制来实现以下场景:
场景可能是(假设线程B稍早进入)
thread A thread B
values 5, 8, 9, 12 7, 8, 9, 13, 14
request 5, 12 7, 8, 9, 13, 14
waits for 8, 9
>>data loaded<<
retrieves 8, 9
>> data loaded <<
returns 5, 8, 9, 12
我应该使用哪种并发机制?
请记住,生产者/消费者不会工作,因为线程A和B不完全是消费者(他们只对某些数据感兴趣)。
由于
答案 0 :(得分:1)
这听起来很像一个锁定管理器,为什么不建立一个呢?
class LockManager<TKey>
{
private Dictionary<TKey, List<EventWaitHandle>> locks =
new Dictionary<TKey, List<EventWaitHandle>>();
private Object syncRoot = new Object();
public void Lock(TKey key)
{
do
{
Monitor.Enter(syncRoot);
List<EventWaitHandle> waiters = null;
if (true == locks.TryGetValue(key, out waiters))
{
// Key is locked, add ourself to waiting list
// Not that this code is not safe under OOM conditions
AutoResetEvent eventLockFree = new AutoResetEvent(false);
waiters.Add(eventLockFree);
Monitor.Exit(syncRoot);
// Now wait for a notification
eventLockFree.WaitOne();
}
else
{
// event is free
waiters = new List<EventWaitHandle>();
locks.Add(key, waiters);
Monitor.Exit(syncRoot);
// we're done
break;
}
} while (true);
}
public void Release(TKey key)
{
List<EventWaitHandle> waiters = null;
lock (syncRoot)
{
if (false == locks.TryGetValue(key, out waiters))
{
Debug.Assert(false, "Releasing a bad lock!");
}
locks.Remove(key);
}
// Notify ALL waiters. Unfair notifications
// are better than FIFO for reasons of lock convoys
foreach (EventWaitHandle waitHandle in waiters)
{
waitHandle.Set();
}
}
}
您必须在使用之前锁定每个值:
class Program
{
class ThreadData
{
public LockManager<int> LockManager { get; set; }
public int[] Work { get; set; }
public AutoResetEvent Done { get; set; }
}
static void Main(string[] args)
{
int[] forA = new int[] {5, 8, 9, 12};
int[] forB = new int[] {7, 8, 9, 13, 14 };
LockManager<int> lockManager = new LockManager<int>();
ThreadData tdA = new ThreadData
{
LockManager = lockManager,
Work = forA,
Done = new AutoResetEvent(false),
};
ThreadData tdB = new ThreadData
{
LockManager = lockManager,
Work = forB,
Done = new AutoResetEvent(false),
};
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), tdA);
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), tdB);
WaitHandle.WaitAll(new WaitHandle[] { tdA.Done, tdB.Done });
}
static void Worker(object args)
{
Debug.Assert(args is ThreadData);
ThreadData data = (ThreadData) args;
try
{
foreach (int key in data.Work)
{
data.LockManager.Lock(key);
Console.WriteLine("~{0}: {1}",
Thread.CurrentThread.ManagedThreadId, key);
// simulate the load the set for Key
Thread.Sleep(1000);
}
foreach (int key in data.Work)
{
// Now free they locked keys
data.LockManager.Release(key);
}
}
catch (Exception e)
{
Debug.Write(e);
}
finally
{
data.Done.Set();
}
}
}
你将面临的最大问题是死锁。将两个数组更改为{5,8,9,7}和{7,8,9,5},您将立即看到我的观点。