等待给定结果的C#多线程并发机制
我需要一种机制来实现以下场景:等待给定结果的C#多线程并发机制,c#,concurrency,C#,Concurrency,我需要一种机制来实现以下场景: 两个或多个线程需要同时加载一组给定的值 每个值只能执行一个请求,因此如果两个线程需要加载相同的子集,那么一个线程必须等待另一个线程 我不想在每个值上都有一个锁(或互斥锁,或另一个原语),因为它们可能太高了 场景可能是(假设线程B稍微提前一点进入) 线程A线程B 值5,8,9,12,7,8,9,13,14 请求5、12、7、8、9、13、14 等待8点,9点 >>数据加载>数据加载这听起来很像锁管理器,为什么不构建一个锁管理器呢 class LockManager&
线程A线程B
值5,8,9,12,7,8,9,13,14
请求5、12、7、8、9、13、14
等待8点,9点
>>数据加载>数据加载这听起来很像锁管理器,为什么不构建一个锁管理器呢
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();
}
}
}
类锁管理器
{
专用字典锁=
新字典();
私有对象syncRoot=新对象();
公用无效锁(TKey key)
{
做
{
Monitor.Enter(syncRoot);
列表等待者=null;
if(true==locks.TryGetValue(key,out waiters))
{
//钥匙已锁定,请将自己添加到等待列表中
//并不是说该代码在OOM条件下不安全
AutoResetEvent eventLockFree=新的AutoResetEvent(假);
waiters.Add(eventLockFree);
Monitor.Exit(syncRoot);
//现在等待通知
eventLockFree.WaitOne();
}
其他的
{
//活动是免费的
waiters=新列表();
锁。添加(钥匙、服务员);
Monitor.Exit(syncRoot);
//我们结束了
打破
}
}虽然(正确);
}
公开无效释放(TKey)
{
列表等待者=null;
锁定(同步根)
{
if(false==locks.TryGetValue(key,out waiters))
{
Assert(false,“释放坏锁!”);
}
锁。取下(钥匙);
}
//通知所有服务员。不公平的通知
//由于锁定车队的原因,其性能优于先进先出(FIFO)
foreach(waiters中的EventWaitHandle waitHandle)
{
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();
}
}
}
类程序
{
类线程数据
{
public LockManager LockManager{get;set;}
公共int[]工作{get;set;}
public AutoResetEvent Done{get;set;}
}
静态void Main(字符串[]参数)
{
int[]forA=新的int[]{5,8,9,12};
int[]forB=新的int[]{7,8,9,13,14};
LockManager LockManager=新的LockManager();
ThreadData tdA=新的ThreadData
{
LockManager=LockManager,
工作=论坛,
完成=新的自动重置事件(错误),
};
ThreadData tdB=新的ThreadData
{
LockManager=LockManager,
工作=forB,
完成=新的自动重置事件(错误),
};
QueueUserWorkItem(新WaitCallback(Worker),tdA);
QueueUserWorkItem(新WaitCallback(Worker),tdB);
WaitHandle.WaitAll(新的WaitHandle[]{tdA.Done,tdB.Done});
}
静态无效辅助对象(对象参数)
{
Assert(args是ThreadData);
ThreadData数据=(ThreadData)参数;
尝试
{
foreach(data.Work中的int键)
{
data.LockManager.Lock(钥匙);
Console.WriteLine(“{0}:{1}”,
Thread.CurrentThread.ManagedThreadId,键);
//模拟为关键点设置的负载
睡眠(1000);
}
foreach(data.Work中的int键)
{
//现在他们把钥匙锁上了
数据.锁管理器.释放(钥匙);
}
}
捕获(例外e)
{
Debug.Write(e);
}
最后
{
data.Done.Set();
}
}
}
你将面临的最大问题是僵局。将这两个数组更改为{5,8,9,7}和{7,8,9,5},您将立即看到我的观点。这些值是不可变的吗?加载一个值需要多长时间?您是说您有一个数据流,该数据流中的每个项目都需要由两个线程处理吗?延迟是一个大问题吗?每个请求都会有自己的编号,大多数情况下它们会被缓存,然后不会被请求。请求是昂贵的,这就是为什么我们只想在加载一次值后执行此操作,请查看任务队列。除非我没有抓住要点,否则您正在创建一个锁列表,每个项一个锁,这正是我想要避免的,因为这会对系统造成过度伤害,对吗?每个服务员一个锁,用于锁定的项。服务生(=线程)一次只能等待一个项目,因此最坏的情况下,您的事件数与线程数一样多(更少,精确匹配意味着您处于死锁状态,但死锁是您需求中固有的,只有通过正确分配集合才能避免),为了避免死锁,策略是:你去交付那些不在等待名单上的,然后等待那些你丢失的,这样可以避免死锁,不是吗?我考虑的另一个选择是:你只需要使用监视器,输入排除,检查“等待名单”上的内容然后从请求中删除已经存在的内容,然后继续,获取剩余元素并等待监视器。每次有人发送东西时,会唤醒所有等待的线程,他们会检查是否有东西给他们,否则(或者直到他们的列表完成)他们会再次睡觉。它的效率不高,因为线程可以被唤醒而什么也不做,但它只使用一个监视器来监视所有事情。你认为呢?这是相似的,不同的是有一个地球仪
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();
}
}
}