При использовании StreamWriter Async я получаю сообщение об ошибке «Поток в данный момент используется предыдущей операцией над потоком» - PullRequest
0 голосов
/ 20 марта 2019

У меня есть набор задач, которые получают строки, которые я пытаюсь координировать в поток. Все выглядит хорошо, но на практике я получаю

Поток в данный момент используется предыдущей операцией в потоке

после Task.WhenAll звонка.

Пример:

    private readonly List<Task<string>> _objectData = new List<Task<string>>();
    private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);

    private async Task SafelyWrite(StreamWriter streamWriter, string field)
    {
        await _writerSemaphore.WaitAsync();

        if (field.IsNullOrWhiteSpace())
        {
            return;
        }

        streamWriter.Write(field);

        await streamWriter.FlushAsync();

        _writerSemaphore.Release();
    }

    public override async Task Build(StreamWriter streamWriter)
    {
        streamWriter.Write('{');

        await Task.WhenAll(
            _objectData.Select(async str => SafelyWrite(streamWriter, await str)));

        // await Task.Delay(10);

        // If I don't wait for a few milliseconds the app
        // will throw an error on this line that the stream
        // is currently being written to?
        streamWriter.Write('}');

        await streamWriter
            .FlushAsync();
    }

С задержкой или без нее, если я посмотрю содержимое потока, все это действительно. Все закончилось, просто StreamWriter думает, что нет? Если я подожду несколько миллисекунд, StreamWriter думает, что все закончилось, и я могу написать закрывающий блок.

Есть ли что-то между Task.WhenAll и SemaphoreSlim, которого мне не хватает?

(Извините за объем кода, казалось, что минимальный сбой, но полезный пример завершен)

1 Ответ

0 голосов
/ 20 марта 2019

Независимо от оправдания применения такого подхода для записи данных в поток проблема здесь в том, что есть место, где вы просто «запускаете и забываете» асинхронные задачи. В частности, если вы посмотрите на _objectData.Select(async str => SafelyWrite(streamWriter, await str)), то точно увидите, что здесь есть перечислитель, который внутри инициирует ожидание str задач, но фактически не ожидает завершения этих процессов на более высоком уровне. Так что вместо

await Task.WhenAll(_objectData.Select(async str => SafelyWrite(streamWriter, await str)));

должно быть

await Task.WhenAll((await Task.WhenAll(_objectData)).Select(str => SafelyWrite(streamWriter, str)));

Просто для удобства чтения и во избежание неправильного толкования второй вариант здесь эквивалентен:

var results = await Task.WhenAll(_objectData);
var writeTasks = results.Select(str => SafelyWrite(streamWriter, str));
await Task.WhenAll(writeTasks);
...