ManualResetEventSlim и Lock - PullRequest
       9

ManualResetEventSlim и Lock

0 голосов
/ 07 января 2011

У меня есть часть данных, для извлечения которой требуется довольно много времени. У меня есть разные способы выяснить, нужно ли получать новые данные или я могу использовать свой текущий «кеш» 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 ]

2 голосов
/ 07 января 2011

Возможно, вы захотите взглянуть на шаблон Future<T>.Одна реализация доступна в библиотеке Magnum: Future.cs .По сути, вы возвращаете Future<T> из одного GetData() метода.Вы можете решить, следует ли возвращать блокирующую или неблокирующую версию вашего Future<T>.Когда вызывающая сторона готова использовать значение, они могут либо проверить, готово ли значение Future, либо просто запросить значение, и Future заблокируется, пока не получит значение.

1 голос
/ 07 января 2011

Я думаю, что ваша инкапсуляция может быть немного подправлена. Например, я думаю, что вы должны разделить код, который асинхронно получает данные, на следующее:

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, либо основанный на событиях), поскольку они позволят вам легче подключаться к другим конвейерам .

...