Не уверен, что это правильный способ асинхронной обработки файла - PullRequest
0 голосов
/ 01 мая 2020

Я использую этот код для записи группы файлов на диск:

var savingTasks = games.Games.Select(t=>{
                var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
                Log.Information($"trying to save game in:{path}");
                var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite);
                opened.Add(fs);
                var sr = new StreamWriter(fs);
                writers.Add(sr);
                var tsk =  sr.WriteAsync(t.Pgn);
                return tsk;
                });
try
{
    await Task.WhenAll(savingTasks);
    var flushing = writers.Select(u=>u.FlushAsync());
    await Task.WhenAll(flushing);
}
catch(Exception e)
{
     Log.Fatal($"Cannot write to file:{e}");
     throw e;
}
finally
{
     opened.ForEach(s => s.Close());
}

В некоторых шагах я не уверен, что у меня все получается лучше, даже если код работает просто хорошо. Часть, которая не убеждает меня в том, как я справляюсь с закрытием: я создал группу задач в Select, но мне пришлось следить за открытым файлом, чтобы закрыть их (см. Наконец), и аналогичным образом, Мне пришлось управлять коллекцией StreamWriter (см. writers). Это меня не убеждает, есть ли лучший подход?

Ответы [ 2 ]

2 голосов
/ 01 мая 2020

Я переместу FlushAsync в finally, потому что, если во время выполнения задач возникнет исключительная ситуация, они не будут очищены. Кроме того, я бы порекомендовал для чистоты сделать все одним способом, например следующим:

var savingTasks = games.Games.Select(t=>ExecuteGameMethod(t));
try
{
     await Task.WhenAll(savingTasks);
}
catch(Exception e)
{
        Log.Fatal($"Cannot write to file:{e}");
        throw;
}


public async Task ExecuteGameMethod(Game game)
{
    var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(game.Url)),"pgn");
    Log.Information($"trying to save game in:{path}");
    using(var fs = new FileStream(path,FileMode.CreateNew,FileAccess.ReadWrite,bufferSize:4096, isAsync:true ))
    using(var sr = new StreamWriter(fs))
    {
        await sr.WriteAsync(game.Pgn);
        await sr.FlushAsync();
    }

}
2 голосов
/ 01 мая 2020

Вы слишком усложняете вещи.

Вы должны использовать using блок для ваших FileStream и StreamWriter, который заботится о промывке / закрытии при их утилизации.

Ожидая WriteAsync, а не возвращая сгенерированный Task, вы гарантируете, что ваши FileStream и StreamWriter не будут утилизированы слишком быстро:

var savingTasks = games.Games
    .Select(async t =>
    {
        var path = Path.ChangeExtension(Path.Combine(savePath,Path.GetFileName(t.Url)),"pgn");
        Log.Information($"trying to save game in:{path}");

        using (var fs = new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
        using (var sr = new StreamWriter(fs))
        {
            await sr.WriteAsync(t.Pgn);
        }
    });

try
{
    await Task.WhenAll(savingTasks);
}
catch (Exception e)
{
    Log.Fatal($"Cannot write to file:{e}");
    throw;
}
...