2014-05-21 21 views
0

我有填充共享集合的方法和我调用它的lock和一个Task这样的:如何在继续之前确保任务已开始?

void PopulateCollection() 
{ 
    Task.Factory.StartNew(() => 
     { 
      lock (_padlock) 
      { 
       _sharedDictionary = GetSharedDictionary(); 
      } 
     }); 
} 

我用同样的lock(_padlock)围绕该集合时,它读取的内容。

我遇到的问题是Task.Factory可能没有启动它的任务(因此可能没有获得锁定)。这导致竞赛状况。

像这样的读者方法存在:

void ReadCollection() 
{ 
    lock(_padlock) 
    { 
     DoSomethingWithCollection(_sharedDictionary); 
    } 
} 

所以我的问题是,我有这样的代码:

... 
PopulateCollection(); 
... // some things happen 
ReadCollection(); 
... 

我不能保证它的读取之前收集填充,因为我在读取集合之前,不能保证任务已经开始(并且因此获得了锁定)。

我不想继续下去,直到获得锁定。

+3

为什么你关心的比赛条件下,如果锁定周围的共享内存? – i3arnon

+0

,因为锁没有及时获得。 – Matthew

+0

如果它是在别的地方获得的,那么它会一直等到锁定空闲。 – i3arnon

回答

4

您没有正确使用TPL,因此您的问题。使用TPL时,您通常不需要非常使用lock

而不是突出您的任务中的共享变量来设置操作的结果,请让该操作的结果设置任务的结果。然后,您可以保留对该任务的引用,并在计算它时使用它的值。

Task<Dictionary<TKey,TValue>> PopulateCollection() 
{ 
    return Task.Factory.StartNew(() => GetSharedDictionary()); 
} 

您可以再附上延续到任务做,结果什么是计算后:

PopulateCollection() 
    .ContinueWith(task => DoSomethingWithCollection(t.Result)); 
+0

我需要在这种情况下使用锁(据我所知)。这个填充方法在构造函数中调用。我不知道集合何时被读取,但是如果它*被读取,我需要它等待来自Populate方法的锁定。这基本上是异步抢先加载。 – Matthew

+1

@Matthew不,你*不需要使用锁。 Task类会为你处理所有的同步。如果您只是有条件地完成工作,而不是简单地存储'Task '并有条件地应用延续。当附加延续时,Task类将负责确保处理不会运行,直到生成完成,并且数据正确传递,而没有任何同步问题。它真的很容易。这就是使用TPL的全部*点,它能够为您处理所有这些问题。 – Servy

+0

也许我需要放入更多的代码......集合被填充到类构造函数中。该类有一个“Get()”方法,返回项目(并等待它们是否尚未填充)......我不明白我可以如何使用您指定的代码来完成它。继续与填充不同。 – Matthew

1

如果你想确保,这里面的任务锁定在任务之外的流动延续前获得,你可以使用的AutoResetEvent:

var waitHandle = new AutoResetEvent(false); 
    Task.Factory.StartNew(() => 
     { 
      lock (_padlock) 
      { 
       waitHandle.Set(); 
       _sharedDictionary = GetSharedDictionary(); 
      } 
     }); 
    waitHandle.WaitOne(); 
+0

对,这里的危险将会是一个僵局 – Matthew

+0

如果您预计会遇到死锁危险,那么您可以采取任何方法来解决您的问题。 “确保获得锁定”总是容易出现死锁。 – Edin

2

我会做的是返回Task,以便您可以等待完成的操作,而不是按照您的建议启动它。喜欢的东西:

Task PopulateCollection() 
{ 
    return Task.Factory.StartNew(() => 
     { 
      lock (_padlock) 
      { 
       _sharedDictionary = GetSharedDictionary(); 
      } 
     }); 
} 
var populateTask = PopulateCollection(); 
... // some things happen 
populateTask.Wait(); 
ReadCollection(); 

如果填充收集一次,然后反复使用,另一种选择是改变_sharedDictionaryTask<YourDictionaryType>

void PopulateCollection() 
{ 
    _sharedDictionaryTask = Task.Factory.StartNew(() => GetSharedDictionary()); 
} 

void ReadCollection() 
{ 
    DoSomethingWithCollection(_sharedDictionaryTask.Result); 
} 

Task会照顾这里需要同步。

+0

我明确*不想*等待它完成。我只想等待获得锁定。 – Matthew

+1

@Matthew但是你只是在等待它的获得,以便你稍后可以等待它完成。这是一个等待它完成的方法,不要求它首先启动。它通过解决你的*实际*问题解决了* need *的问题。 – Servy

+0

@Servy我想我现在正在理解。我的参考成为任务而不是由任务填充的收集。 – Matthew

相关问题