我有一组任务,这些任务获取要尝试协调到流中的字符串。一切看起来不错,但实际上我得到了
该流的先前操作当前正在使用该流
在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
之间是否缺少我想要的东西?
(很抱歉,代码量最少,示例似乎很失败,但很有用)
答案 0 :(得分:-1)
不管采用这种将数据写入流中的方法的合理性,这里的问题都是存在一个地方,您只是“解雇”了异步任务。尤其是,如果您仔细查看_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);