Правильный способ использования DisposeAsync в потоках C # - PullRequest
3 голосов
/ 04 ноября 2019

Я пишу метод, который асинхронно записывает отдельные строки текста в файл. Если он отменен , он удаляет созданный файл и выпрыгивает из цикла.

Это упрощенный код, который отлично работает ... И я отметил 2 пункта, которые я не уверен, какони обрабатываются. Я хочу, чтобы код не блокировал поток в любом случае.

public async Task<IErrorResult> WriteToFileAsync(string filePath,
                                                 CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    using var stream = new FileStream(filePath, FileMode.Create);
    using var writer = new StreamWriter(stream, Encoding.UTF8);

    foreach (var line in Lines)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            //
            // [1] close, delete and throw if cancelled
            //
            writer.Close();
            stream.Close();
            if (File.Exists(filePath))
                File.Delete(filePath);
            throw new OperationCanceledException();
        }

        // write to the stream
        await writer.WriteLineAsync(line.ToString());
    }

    //
    // [2] flush and let them dispose
    //
    await writer.FlushAsync();
    await stream.FlushAsync();
    // await stream.DisposeAsync();
    return null;
}

1

Я звоню Close() на FileStream и StreamWriter, и я думаю, что он будет работатьсинхронно и блокирует поток. Как я могу улучшить это? Я не хочу ждать, пока он сбросит буфер в файл и затем удалит файл.

2

Полагаю, будет вызван метод Dispose, а не DisposeAsyncв конце области видимости using. ( верно ли это предположение? ).

Таким образом, Dispose блокирует поток и для предотвращения того, что я сначала очищаюсь с FlushAsync, чтобы Dispose выполнил меньше вещей. ( в какой степени это правда? )

Я также мог бы удалить using, и вместо этого я мог бы написать DisposeAsync вручную в этих двух местах. Но это уменьшит читабельность.

Если я открою FileStream с помощью useAsync = true, он автоматически вызовет DisposeAsync, когда закончится блок using?


Любое объяснение илиприветствуется вариант вышеупомянутого кода, который работает лучше.

1 Ответ

5 голосов
/ 04 ноября 2019

Как вы понимаете, оператор using будет вызывать Dispose(), а не DisposeAsync().

C # 8 принес новый синтаксис await using, но по какой-то причине он не упоминается в Что нового в C # 8.0 article.

Но это , упомянутое в другом месте .

await using var stream = new FileStream(filePath, FileMode.Create);
await using var writer = new StreamWriter(stream, Encoding.UTF8);

Но также обратите внимание, что это будет работать, только если:

  • Вы используете .NET Core 3.0+, так как именно тогда IAsyncDisposable был представлен, или
  • Установите Microsoft.Bcl.AsyncInterfaces NuGet пакет. Хотя это только добавляет интерфейсы и не включает версии Stream типов (FileStream, StreamWriter и т. Д.), Которые его используют.

Даже в Объявляя статью .NET Core 3.0 , IAsyncDisposable упоминается только мимоходом и никогда не раскрывается.

С другой стороны, вам не нужно делать это (я вижупочему сейчас):

writer.Close();
stream.Close();

Поскольку документация для Close гласит:

Этот метод вызывает Dispose, указывая true для освобождения всех ресурсов,Вам не нужно специально вызывать метод Close. Вместо этого убедитесь, что каждый объект Stream правильно расположен.

Поскольку вы используете using, Dispose() (или DisposeAsync()) будет вызываться автоматически, а Close не будетвсе, что еще не происходит.

Так что, если вам нужно конкретно закрыть файл, но вы хотите сделать это асинхронно, просто вызовите DisposeAsync() вместо этого. Он делает то же самое.

await writer.DisposeAsync();

public async Task<IErrorResult> WriteToFileAsync(string filePath,
                                                 CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    await using var stream = new FileStream(filePath, FileMode.Create);
    await using var writer = new StreamWriter(stream, Encoding.UTF8);

    foreach (var line in Lines)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            // not possible to discard, FlushAsync is covered in DisposeAsync
            await writer.DisposeAsync(); // use DisposeAsync instead of Close to not block

            if (File.Exists(filePath))
                File.Delete(filePath);
            throw new OperationCanceledException();
        }

        // write to the stream
        await writer.WriteLineAsync(line.ToString());
    }

    // FlushAsync is covered in DisposeAsync
    return null;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...