Подтвердить что ты не робот

Обертка ManualResetEvent как ожидаемая задача

Я бы хотел подождать в ручном мероприятии reset с тайм-аутом и отменой аннулирования. Я придумал что-то вроде ниже. Ручной объект события reset предоставляется API, находящимся под моим контролем. Есть ли способ сделать это без принятия и блокировки потока из ThreadPool?

static Task<bool> TaskFromWaitHandle(WaitHandle mre, int timeout, CancellationToken ct)
{
    return Task.Run(() =>
    {
        bool s = WaitHandle.WaitAny(new WaitHandle[] { mre, ct.WaitHandle }, timeout) == 0;
        ct.ThrowIfCancellationRequested();
        return s;
    }, ct);
}

// ...

if (await TaskFromWaitHandle(manualResetEvent, 1000, cts.Token))
{
    // true if event was set
}
else 
{
    // false if timed out, exception if cancelled 
}

[EDITED] По-видимому, имеет смысл использовать RegisterWaitForSingleObject. Я попробую.

4b9b3361

Ответ 1

RegisterWaitForSingleObject объединяет ожидания на выделенные потоки официанта, каждый из которых может ждать на нескольких дескрипторах (в частности, 63 из них, MAXIMUM_WAIT_OBJECTS минус один для дескриптора управления).

Итак, вы должны иметь возможность использовать что-то вроде этого (предупреждение: untested):

public static class WaitHandleExtensions
{
    public static Task AsTask(this WaitHandle handle)
    {
        return AsTask(handle, Timeout.InfiniteTimeSpan);
    }

    public static Task AsTask(this WaitHandle handle, TimeSpan timeout)
    {
        var tcs = new TaskCompletionSource<object>();
        var registration = ThreadPool.RegisterWaitForSingleObject(handle, (state, timedOut) =>
        {
            var localTcs = (TaskCompletionSource<object>)state;
            if (timedOut)
                localTcs.TrySetCanceled();
            else
                localTcs.TrySetResult(null);
        }, tcs, timeout, executeOnlyOnce: true);
        tcs.Task.ContinueWith((_, state) => ((RegisteredWaitHandle)state).Unregister(null), registration, TaskScheduler.Default);
        return tcs.Task;
    }
}

Ответ 2

Вы также можете использовать SemaphoreSlim.WaitAsync(), который похож на ManualResetEvent