C# WaitHandle.WaitAny,允许线程有序进入
我有固定数量的“浏览器”,每个浏览器都不是线程安全的,因此必须在单个线程上使用。另一方面,我有一长串等待使用这些浏览器的线程。我目前正在做的是拥有一个C# WaitHandle.WaitAny,允许线程有序进入,c#,multithreading,autoresetevent,C#,Multithreading,Autoresetevent,我有固定数量的“浏览器”,每个浏览器都不是线程安全的,因此必须在单个线程上使用。另一方面,我有一长串等待使用这些浏览器的线程。我目前正在做的是拥有一个AutoResetEvent数组: public readonly AutoResetEvent[] WaitHandles; 并按如下方式初始化它们: WaitHandles = Enumerable.Range(0, Browsers.Count).Select(_ => new AutoResetEvent(true)).ToArra
AutoResetEvent
数组:
public readonly AutoResetEvent[] WaitHandles;
并按如下方式初始化它们:
WaitHandles = Enumerable.Range(0, Browsers.Count).Select(_ => new AutoResetEvent(true)).ToArray();
因此,每个浏览器都有一个AutoResetEvent
,它允许我为每个线程检索特定的浏览器索引:
public Context WaitForBrowser(int i)
{
System.Diagnostics.Debug.WriteLine($">>> WILL WAIT: {i}");
var index = WaitHandle.WaitAny(WaitHandles);
System.Diagnostics.Debug.WriteLine($">>> ENTERED: {i}");
return new Context(Browsers[index], WaitHandles[index]);
}
这里的i
只是等待线程的索引,因为这些线程位于列表中,并且具有特定的顺序。我只是出于调试的目的传递它Context
是一个一次性的,当释放时,它会在等待句柄上调用Set
当我查看输出时,我看到所有“>>>将等待:{I}”消息的顺序都是正确的,因为对WaitForBrowser
的调用是按顺序进行的,但是我的“>>>输入:{I}”消息是随机顺序的(前几个除外),因此它们的输入顺序与到达var index=WaitHandle.WaitAny的顺序不同(WaitHandler);
行
所以我的问题是,有没有办法修改它,使线程以相同的顺序输入调用的
WaitForBrowser
方法(这样“>>>输入:{i}”消息也被排序)?您考虑过使用信号量而不是AutoResetEvent数组吗
这里讨论了等待线程(信号量)的顺序问题:
由于似乎没有现成的解决方案,我最终使用了以下的修改版本:
公共类信号量equalItem:IDisposable
{
私人住宅;
私有只读EventWaitHandle WaitHandle;
公共只读资源;
公共信号量equeueItem(EventWaitHandle waitHandle,T资源)
{
WaitHandle=WaitHandle;
资源=资源;
}
公共空间处置()
{
如果(!已处置)
{
这是真的;
WaitHandle.Set();
}
}
}
公共类信号量均衡:IDisposable
{
私有只读T[]资源;
私有只读AutoResetEvent[]WaitHandles;
私人住宅;
私有ConcurrentQueue=新ConcurrentQueue();
公共信号量(T[]资源)
{
资源=资源;
WaitHandles=Enumerable.Range(0,resources.Length).Select(=>newautoresetEvent(true)).ToArray();
}
公共信号量队列等待(CancellationToken CancellationToken)
{
返回WaitAsync(cancellationToken).Result;
}
公共任务WaitAsync(CancellationToken CancellationToken)
{
var tcs=new TaskCompletionSource();
排队;排队(tcs);
Task.Run(()=>WaitHandle.WaitAny(WaitHandles.Concat(new[]{cancellationToken.WaitHandle}).ToArray()).ContinueWith(Task=>
{
if(Queue.TryDequeue(弹出的out变量))
{
var指数=任务结果;
if(cancellationToken.IsCancellationRequested)
popped.SetResult(空);
其他的
SetResult(新的信号量equeueItem(WaitHandles[index],Resources[index]);
}
});
返回tcs.Task;
}
公共空间处置()
{
如果(!已处置)
{
foreach(WaitHandles中的变量句柄)
handle.Dispose();
这是真的;
}
}
}
我有,但我不知道如何获取索引(WaitOne方法返回一个bool
)。@JohnWu的可能重复项不是同一个问题(参见问题正文)
public class SemaphoreQueueItem<T> : IDisposable
{
private bool Disposed;
private readonly EventWaitHandle WaitHandle;
public readonly T Resource;
public SemaphoreQueueItem(EventWaitHandle waitHandle, T resource)
{
WaitHandle = waitHandle;
Resource = resource;
}
public void Dispose()
{
if (!Disposed)
{
Disposed = true;
WaitHandle.Set();
}
}
}
public class SemaphoreQueue<T> : IDisposable
{
private readonly T[] Resources;
private readonly AutoResetEvent[] WaitHandles;
private bool Disposed;
private ConcurrentQueue<TaskCompletionSource<SemaphoreQueueItem<T>>> Queue = new ConcurrentQueue<TaskCompletionSource<SemaphoreQueueItem<T>>>();
public SemaphoreQueue(T[] resources)
{
Resources = resources;
WaitHandles = Enumerable.Range(0, resources.Length).Select(_ => new AutoResetEvent(true)).ToArray();
}
public SemaphoreQueueItem<T> Wait(CancellationToken cancellationToken)
{
return WaitAsync(cancellationToken).Result;
}
public Task<SemaphoreQueueItem<T>> WaitAsync(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<SemaphoreQueueItem<T>>();
Queue.Enqueue(tcs);
Task.Run(() => WaitHandle.WaitAny(WaitHandles.Concat(new[] { cancellationToken.WaitHandle }).ToArray())).ContinueWith(task =>
{
if (Queue.TryDequeue(out var popped))
{
var index = task.Result;
if (cancellationToken.IsCancellationRequested)
popped.SetResult(null);
else
popped.SetResult(new SemaphoreQueueItem<T>(WaitHandles[index], Resources[index]));
}
});
return tcs.Task;
}
public void Dispose()
{
if (!Disposed)
{
foreach (var handle in WaitHandles)
handle.Dispose();
Disposed = true;
}
}
}