Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/heroku/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WaitHandle.WaitAny,允许线程有序进入_C#_Multithreading_Autoresetevent - Fatal编程技术网

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;
        }
    }
}