我有一组任务,可以将我尝试协调的字符串转换为流。这一切看起来都不错,但实际上我得到了一个
该流当前正在流上的先前操作中使用
在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
之间有什么东西我错过了吗?
(对不起代码量,它似乎是最小的失败,但有用的完整,例子)
无论应用这种方法将数据写入流的理由,这里的问题都存在于您只是“发射并忘记”异步任务的地方。特别是如果你仔细看看_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);