0

我有一段需要花费大量时间才能获取的数据。我有不同的方法来确定是否应该获取新数据,或者我是否可以使用我当前的“缓存”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(...){}?

4

2 回答 2

2

你可能想研究一下这个Future<T>模式。Magnum 库中提供了一种实现:Future.cs。基本上,您Future<T>从单个GetData()方法返回 a 。您可以决定是返回阻塞版本还是非阻塞版本的Future<T>. 当调用者准备好使用该值时,他们可以检查 Future 的值是否已准备好,或者只是询问该值,Future 将阻塞直到它获得该值。

于 2011-01-07T13:46:16.413 回答
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 中公开的传统异步模式(Begin/End 模式或基于事件的模式),因为它们可以让您更轻松地插入其他管道。

于 2011-01-07T14:48:56.927 回答