【发布时间】:2018-12-17 20:38:13
【问题描述】:
如果该任务是为相同的输入创建的,我想在其中等待正在进行的任务的这段代码。这是我正在做的事情的最小复制。
private static ConcurrentDictionary<int, Task<int>> _tasks = new ConcurrentDictionary<int, Task<int>>();
private readonly ExternalService _service;
public async Task SampleTask(){
var result = await _service.DoSomething();
await Task.Delay(1000) //this task takes some time do finish
return result;
}
public async Task<int> DoTask(int key) {
var task = _tasks.GetOrAdd(key, _ => SampleTask());
var taskResult = await task;
_tasks.TryRemove(key, out task);
return taskResult;
}
我正在编写一个测试,以确保当多个请求想要在(大致)同时执行任务时等待相同的任务。我通过模拟_service 并计算_service.DoSomething() 被调用的次数来做到这一点。如果对DoTask(int key) 的调用大致在同一时间进行,则应该只有一次。
但是,结果显示,如果我多次调用 DoTask(int key) 且调用之间的延迟小于 1~2 毫秒,则两个任务都将创建并执行 SampleTask() 的实例,而第二个任务将替换字典中的第一个。
考虑到这一点,我们可以说这个方法是真正的线程安全的吗?或者我的问题本身不是线程安全的问题吗?
【问题讨论】:
-
当您调用
GetOrAdd()时,您传递给它的委托在某些情况下可能会被多次调用。这是你看到的吗? -
你也不在等待
SampleTask() -
@MatthewWatson,那会是什么情况?
-
@Tseng,我需要在工厂代表处等待吗?在它被分配给
task变量后,我正在等待它 -
@LuísGabrieldeAndrade 你一开始想做什么?
ConcurrentDictionary是线程安全的,但这并不意味着它包含的对象是线程安全的。它也不是任务队列。如果您想处理多条消息,您可能应该使用ActionBlock<T>或 TPL DataflowBlock 库中的其他类。另一个不错的选择是使用System.Threading.Channels来实现发布/订阅工作者。