如何确保任务在继续之前已经开始
本文关键字:继续 开始 任务 何确保 确保 | 更新日期: 2023-09-27 18:04:23
我有一个填充共享集合的方法,我在lock
和Task
中调用它,如下所示:
void PopulateCollection()
{
Task.Factory.StartNew(() =>
{
lock (_padlock)
{
_sharedDictionary = GetSharedDictionary();
}
});
}
无论何时读取,我都使用相同的lock(_padlock)
。
我遇到的问题是Task.Factory
可能没有启动它的任务(因此可能没有获得锁)。这将导致竞态条件。
存在这样的reader方法:
void ReadCollection()
{
lock(_padlock)
{
DoSomethingWithCollection(_sharedDictionary);
}
}
所以我的问题是我有这样的代码:
...
PopulateCollection();
... // some things happen
ReadCollection();
...
我不能保证在读取集合之前填充它,因为我不能保证在读取集合之前Task已经启动(因此锁已经获得)。
我不想在获得锁之前继续。
您一开始就没有正确使用TPL,因此出现了问题。在使用TPL时,通常不需要经常使用lock
。
与其改变任务中的共享变量来设置操作的结果,不如让该操作的结果设置任务的结果。然后,您可以保留对该任务的引用,并在计算该任务时使用其值。
Task<Dictionary<TKey,TValue>> PopulateCollection()
{
return Task.Factory.StartNew(() => GetSharedDictionary());
}
然后,您可以为该任务附加一个延续,以便在计算结果后对其进行处理:
PopulateCollection()
.ContinueWith(task => DoSomethingWithCollection(t.Result));
我要做的是返回Task
,以便您可以等待操作完成,而不是像您建议的那样开始。比如:
Task PopulateCollection()
{
return Task.Factory.StartNew(() =>
{
lock (_padlock)
{
_sharedDictionary = GetSharedDictionary();
}
});
}
var populateTask = PopulateCollection();
... // some things happen
populateTask.Wait();
ReadCollection();
如果您只填充一次集合,然后重复使用它,另一种选择是将_sharedDictionary
更改为Task<YourDictionaryType>
:
void PopulateCollection()
{
_sharedDictionaryTask = Task.Factory.StartNew(() => GetSharedDictionary());
}
void ReadCollection()
{
DoSomethingWithCollection(_sharedDictionaryTask.Result);
}
Task
将负责必要的同步
如果您希望确保在任务外部的流继续之前获得任务内部的锁,您可以使用AutoResetEvent:
var waitHandle = new AutoResetEvent(false);
Task.Factory.StartNew(() =>
{
lock (_padlock)
{
waitHandle.Set();
_sharedDictionary = GetSharedDictionary();
}
});
waitHandle.WaitOne();