Как ждать несколько массивов WaitHandle в асинхронном режиме в C # с поддержкой отмены? - PullRequest
0 голосов
/ 05 мая 2019

C # имеет рекомендуемый метод для ожидания одного объекта WaitHandle неблокирующим способом до ThreadPool.RegisterWaitForSingleObject.Но мне нужно нечто подобное, но для нескольких объектов.Что-то вроде WaitHandle.WaitAll но в асинхронном варианте.Как добиться этого менее затратным способом?Теперь я думаю о создании задачи и жду там ручек, примерно так:

public static class WaitHandleExtension
{
    public static Task<bool> WaitAllAsync ( this WaitHandle[] handles )
    {
        return Task.Factory.StartNew( () => WaitHandle.WaitAll( handles ) );
    }
}

Но я не могу реализовать отмену в таком подходе.Как лучше подождать, пока все дескрипторы будут асинхронными, с поддержкой отмены?

UPD: здесь я опишу решаемую мной задачу, может быть, кто-то предложит другой способ ее реализации в асинхронном режиме:

Я реализую кроссплатформенный поток IPC, который работает как именованный канал, но в упрощенном виде.Он должен быть полностью асинхронным и поддерживать отмену.Он должен поддерживать Mono в macOS и Windows, потому что серверное приложение использует Mono и не может использовать .NET в Windows.Проблема в том, что Mono в Windows вообще не поддерживает доменные сокеты UNIX, и реализация именованных каналов не завершена (соединение с именованным каналом не может быть отменено в Mono Windows).Я реализовал часть macOS в доменных сокетах UNIX, и она прекрасно работает.В Windows я решил реализовать свой собственный класс Stream на основе Memory Mapped File.Он имеет два общих буфера чтения-записи для двустороннего обмена данными.Каждый буфер защищен именованным семафором и имеет два именованных события EventWaitHandle: один сигнализирует о том, что буфер не пуст, а другой о том, что буфер не заполнен.Таким образом, метод Send ожидает семафора и неполного события, чтобы заполнить буфер данными и сигнализировать непустое событие, если буфер выполнен, он сбрасывает неполное событие.Read ожидает, когда семафор и непустое событие получат данные и сообщат о неполном событии, если он использует все данные, сбрасывает непустое событие.Он отлично работает синхронно.Но было бы здорово реализовать отменяемые ReadAsync и WriteAsync.

1 Ответ

0 голосов
/ 05 мая 2019

Это работает, но чувствует себя немного грязно. Идея состоит в том, чтобы эффективно дождаться завершения любого дескриптора, включая дескриптор маркера отмены. Если один из дескрипторов (кроме токена отмены) завершен, удалите его из списка оставшихся дескрипторов и снова подождите, пока все дескрипторы не будут завершены ИЛИ не будет завершен дескриптор ожидания токена отмены.

static async Task<bool> WaitForHandlesAsync(WaitHandle[] handles, CancellationToken cancellationToken) {
    List<WaitHandle> remainingHandles = new List<WaitHandle>(handles.Length + 1);
    remainingHandles.Add(cancellationToken.WaitHandle);
    remainingHandles.AddRange(handles);
    bool canceled = cancellationToken.IsCancellationRequested;
    while (remainingHandles.Count > 1 && !canceled) {
        int idx = await Task.Factory.StartNew(() => WaitHandle.WaitAny(remainingHandles.ToArray()));
        if (idx == 0)
            canceled = true;
        else {
            remainingHandles.RemoveAt(idx);
        }
    }
    return !canceled;
}
...