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>パターンを調べたいと思うかもしれません。1つの実装がMagnumライブラリで利用可能です:Future.cs。基本的に、Future<T>単一のGetData()メソッドからを返します。のブロッキングバージョンと非ブロッキングバージョンのどちらを返すかを決定できます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 に答える