简体   繁体   English

ManualResetEventSlim和锁定

[英]ManualResetEventSlim and Lock

I have a piece of data that takes quite a lot of time to fetch. 我有一条数据需要花费很多时间来获取。 I have different ways of figuring out if new data should be fetched or if I can use my current "cache" theResult When someone asks for that piece of data I want to be able to both do a blocking and non blocking return. 我有不同的方法来弄清楚是否应该提取新数据,或者我是否可以使用当前的“缓存” theResult当某人请求该数据时,我希望能够进行阻塞和非阻塞返回。

Im not sure what the best way is to do that, I was considering something with ManualResetEventSlim and a lock: 我不确定最好的方法是什么,我在考虑使用ManualResetEventSlim和一个锁:

NonBlocking: 非阻塞:

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;
   }
}

Blocking: 封锁:

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;
}

But I'm not certain that is the way to do a thing like that. 但是我不确定这是做这样的事情的方法。 For example the _resetEvent.Wait() inside a lock(...){} ? 例如,在lock(...){}_resetEvent.Wait()吗?

You might want to look into the Future<T> pattern. 您可能需要研究Future<T>模式。 One implementation is available in the Magnum library: Future.cs . Magnum库中提供了一种实现: Future.cs Basically, you return a Future<T> from a single GetData() method. 基本上,您从单个GetData()方法返回Future<T> You can decide whether to return the blocking or non-blocking version of your Future<T> . 您可以决定是否返回Future<T>的阻止版本或非阻止版本。 When the caller is ready to use the value, they can either check if the Future's value is ready or just ask for the Value and the Future will block until it gets the value. 当调用者准备使用该值时,他们可以检查Future的值是否准备好,也可以仅询问该Value,并且Future会阻塞直到获得该值。

I think that your encapsulation could use a little tweaking. 我认为您的封装可能需要一些调整。 For example, I think that you should separate the code that gets the data asynchronously to just this: 例如,我认为您应该将异步获取数据的代码与此分开:

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

Then, your class instance can worry separately about the state, and use the Task<T> to facilitate that: 然后,您的类实例可以分别担心状态,并使用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;
       }
    }
}

Now, because you are using Task<T> , your GetDataBlocking (I think it should be called GetData ) method becomes very simple: 现在,由于您正在使用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();
}

In the end, I think you should keep more in line with the traditional async patterns exposed in .NET (either the Begin/End pattern, or event-based), as they will allow you to plug into other pipelines more easily. 最后,我认为您应该更加与.NET中公开的传统异步模式(Begin / End模式或基于事件的模式)保持一致,因为它们可以使您更轻松地插入其他管道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM