ManualResetEventSlim和Lock

时间:2011-01-07 13:35:41

标签: c# multithreading manualresetevent

我有一段数据,需要花费大量时间才能获取。我有不同的方法来确定是否应该提取新数据或者我是否可以使用我当前的“缓存”theResult 当有人要求提供这段数据时,我希望能够进行阻止和非阻塞返回。

我不确定最好的方法是做什么,我正在考虑使用ManualResetEventSlim和一个锁:

非阻塞:

theState = State.None;

public Data GetDataNonBlocking(){

   lock(_myLock){
        if (theState == State.Getting)
          return null;
        if (theState == State.Complete
          return theData;

        theState = State.Getting;
        _resetEvent.Reset();
        Task.Factory.StartNew(
           ()=>{                     
                 //<...Getting data.....>
                 theData= ...data....;
                 lock(_myLock){
                    theState = State.Complete;
                   _resetevent.Set();  
                 }
                });
         return null;
   }
}

封闭:

public Data GetDataBlocking(){

  lock(_myLock){
       if (theState == State.Getting){
           _resetevent.Wait();
           return theData;
       }
       if (theState == State.Complete)
          return theData;

       _resetevent.Reset();
       theState = State.Getting;
  }
  //.....
  theData= 1234;
  lock(_myLock){
     State = State.Complete;
     _resetevent.Set();
  }
  return theData;
}

但我不确定那是做这样的事情的方法。例如_resetEvent.Wait()内的lock(...){}

2 个答案:

答案 0 :(得分:2)

您可能希望查看Future<T>模式。 Magnum库中提供了一种实现:Future.cs。基本上,您从单个Future<T>方法返回GetData()。您可以决定是否返回Future<T>的阻止版或非阻止版。当调用者准备使用该值时,他们可以检查Future的值是否准备就绪,或者只是询问Value并且Future将阻塞,直到获得该值。

答案 1 :(得分:1)

我认为您的封装可能会稍微调整一下。例如,我认为你应该将获取数据的代码异步分离到这个:

static class DataFactory
{
    internal static DataType GetData()
    {
        // Return the data.
        return new DataType();
    }    
}

然后,您的类实例可以单独担心状态,并使用Task<T>来促进:

class DataManager
{
    // The lock on the operation.
    private readonly object lockObj = new object();

    // The state.
    private State theState = State.None;

    // The task to get the state.
    private Task<DataType> getDataTask;

    public DataType GetDataAsync()
    {        
       lock(lockObj)
       {
           if (theState == State.Getting)
               return null;
           if (theState == State.Complete
               return getDataTask.Result;

           // Set the state to getting.
           theState = State.Getting;

           // Start the task.
           getDataTask = Task.Factory.StartNew(() => {                     
               // Get the data.
               DataType result = DataFactory.GetData();

               // Lock and set the state.
               lock (lockObj)
               {
                   // Set the state.
                   theState = State.Complete;
               }

               // Return the result.
               return result;
           });

           // Return null to indicate the operation started
           // (is in "getting" state).
           return null;
       }
    }
}

现在,因为您使用的是Task<T>,所以GetDataBlocking(我认为它应该被称为GetData)方法变得非常简单:

public DataType GetData()
{
    // Get the data async, if the result is non null, then
    // return it.
    DataType result = GetDataAsync();

    // If it is non-null, return it.
    if (result != null) return result;

    // If at this point, the operation has been kicked off
    // to load the data.  Just wait on the task and return the result then.
    getDataTask.Wait();

    // Return the async data again, it will just return the
    // result from the task.
    return GetDataAsync();
}

最后,我认为你应该更多地与.NET中公开的传统异步模式(开始/结束模式或基于事件)保持一致,因为它们将允许您更容易地插入其他管道

相关问题