2011-01-07 2 views
0

Ich habe ein Stück Daten, die ziemlich viel Zeit zum Abrufen dauert. Ich habe verschiedene Möglichkeiten, herauszufinden, ob neue Daten geholt werden sollen oder ob ich meinen aktuellen "Cache" verwenden kann. theResult Wenn jemand nach diesem Datenelement fragt, kann ich sowohl eine blockierende als auch eine blockierungsfreie Rückkehr durchführen.ManualResetEventSlim und Lock

Ich bin nicht sicher, was der beste Weg, das zu tun ist, ich erwäge etwas mit ManualResetEventSlim und eine Sperre:

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

Aber ich bin mir nicht sicher, ob das so ist. Zum Beispiel die _resetEvent.Wait() innerhalb einer lock(...){}?

Antwort

1

Ich denke, dass Ihre Verkapselung ein wenig optimieren könnte. Zum Beispiel denke ich, dass Sie den Code trennen sollte, der die Daten asynchron gerade diese bekommt:

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

Dann können Sie Ihre Klasseninstanz separat über den Zustand kümmern, und verwenden Sie die Task<T> zu erleichtern, dass:

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

Jetzt, da Sie Task<T> verwenden, Ihre GetDataBlocking (ich glaube, es GetData genannt werden sollte) Methode wird sehr einfach:

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

Am Ende denke ich, dass Sie mehr in Einklang mit den traditionellen asynchronen Mustern in .NET (entweder das Start-/Ende-Muster oder ereignisbasiert) halten sollten, da sie Ihnen ermöglichen, mehr in andere Pipelines einstecken leicht.

2

Sie möchten vielleicht in das Future<T> Muster schauen. Eine Implementierung ist in der Magnum-Bibliothek verfügbar: Future.cs. Grundsätzlich geben Sie eine Future<T> von einer einzigen GetData() Methode zurück. Sie können entscheiden, ob Sie die blockierende oder nicht blockierende Version Ihres Future<T> zurückgeben möchten. Wenn der Aufrufer bereit ist, den Wert zu verwenden, kann er entweder prüfen, ob der Wert der Zukunft bereit ist, oder einfach nach dem Wert fragen, und die Zukunft wird blockieren, bis er den Wert erhält.

+1

Oder, wenn in .NET 4.0, die 'Task ' Klasse. – casperOne