Избавьтесь от блокирующего кода

Я не уверен, что смогу хорошо объяснить это здесь, но позвольте мне попробовать. Код ниже просто показывает идею, поскольку я просто набираю его здесь, не проверяя все в компиляторе.

У меня есть метод (скажем, Method1), в котором я делаю две вещи. 1. Вызов метода веб-сервиса для выполнения действия. 2. После этого я опрашиваю тот же объект веб-службы, чтобы дождаться, пока станут доступны результаты действий.

Итак, в основном что-то вроде следующего.

public class MyClass   
{   
  Method1()   
  {     
    // call Web service method  

    // code for polling    
  }  
}

Однако этот подход блокирует метод Method1 до тех пор, пока не станут доступны результаты действий, определенные во время опроса. Я хочу изменить его таким образом, чтобы вызывающий метод Method1 мог вернуться сразу после вызова метода веб-службы и мог каким-то образом сигнализировать, как только результаты веб-службы будут доступны. Поэтому я думаю о том, чтобы иметь объект AutoResetEvent в качестве члена класса, который я установил в сигнальное состояние во время опроса.

public class MyClass   
{   
  public AutoResetEvent autoEvent = new AutoResetEvent(false);
  Method1()   
  {     
    // call Web service method  

  }  

  Method2()   
  {     
    // code for polling    
    // change state of autoEvent to signaled once results are availble

  }  

}

При таком подходе пользователь этого API просто вызывает Method1, который немедленно возвращается, и он может просто ждать, пока autoEvent получит сигнал. Проблема в том, кто в этом случае будет вызывать Method2?


person BKS    schedule 01.10.2014    source источник
comment
Если вы собираетесь чего-то ждать, вы будете блокироваться, независимо от того, находится ли это в методе 1 или методе 2. Что-то блокируется, когда вы ожидаете события. Настоящая проблема здесь в том, что вы, кажется, вообще не понимаете, как асинхронность работает в .NET, и просто придумываете это по ходу дела. Я предлагаю вам начать с изучения асинхронного шаблона Begin/End, и если вы можете использовать .net 4.5, то прочитайте async/await.   -  person Erik Funkenbusch    schedule 01.10.2014
comment
Я был бы склонен просто позволить Method1() заблокировать. Если это проблема для вызывающего абонента, то именно здесь ее следует обрабатывать, например. использование отдельного потока с любым методом синхронизации, подходящим на более высоком уровне.   -  person HABO    schedule 01.10.2014
comment
@ Эрик Спасибо за ваши комментарии. Я должен признать, что я новичок в асинхронном паттерне. Я обязательно прочитаю о предложенных вами темах, но вы предлагаете мне реализовать здесь асинхронный шаблон Begin/End. Пока мне придется придерживаться .NET 4.0.   -  person BKS    schedule 01.10.2014


Ответы (1)


Существует несколько различных методологий для асинхронного кода. Наиболее распространенным в .NET сегодня является использование задач. Для базового примера вы можете сделать что-то вроде:

Task task = Task.Run(() => DoSomething());
task.ContinueWith(() => DoAfterSomething());

Это позволяет коду продолжать обработку, пока задача выполняется в фоновом потоке. Это может показаться немного многословным и неуклюжим со всеми лямбда-выражениями и подметодами, поэтому был введен шаблон async\await. Это выглядит примерно так:

Task<int> DoSomethingAsync()
{
    // do something asynchronously..
}

async Task<int> ExampleAsync()
{
   int result = await DoSomethingAsync();
   DoAfterSomething();
   return 42;
}

Обратите внимание, что DoAfterSomething может вызываться в потоке, отличном от исходного вызывающего объекта.

Еще одна широко используемая методология – методология APM. Это включает в себя два метода, которые запускают асинхронный поток, и еще один для его завершения (обычно BeginX и EndX). Обычно это выглядит так:

FileStream fStream = /* etc */
byte[] buffer = /* etc */
void ReadFile(FileStream fStream)
{
    var asyncResult = fStream.BeginRead(buffer, 0 , buffer.Length, new AsyncCallback(EndReadCallback), null);
}

void EndReadCallback(IAsyncResult result)
{
    int length = fStream.EndRead(result);
    // continue processing on buffer
}

Другой метод — это модель EAP, которая использует события для инициирования завершения. Эта модель редко используется в среде .NET, но она присутствует. Используется что-то вроде:

WebDownload download = /* etc */
download.DownloadComplete += (buffer) => { /* do something with buffer */ };
download.Get("http://foo/img.jpg");

Лично я использую async\await для всего, что я создаю, но полезно знать и другие методологии, если вы столкнетесь с ними в устаревших компонентах. Если вам когда-нибудь понадобится преобразовать APM или EAP в основанный на задачах, вы можете легко сделать это с помощью Task.Factory.FromAsync или с помощью Источник завершения задачи

person jt000    schedule 01.10.2014