如何确保任务在继续之前已经开始

本文关键字:继续 开始 任务 何确保 确保 | 更新日期: 2023-09-27 18:04:23

我有一个填充共享集合的方法,我在lockTask中调用它,如下所示:

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();