2012-05-21 5 views
1

У меня есть целевая страница на веб-сайте, над которым я работаю, и на действие для этой страницы вызывается несколько асинхронных веб-вызовов, чтобы кэшировать результаты на потом. Тем не менее, я хочу, чтобы до завершения вызовов дошли до следующего действия. В принципе у меня есть:Асинхронные вызовы - как подождать их в потоке пользовательского интерфейса

GetParticipantInfo(planID, partID); 

SetCurrentInvestments(partID, planID); 

GetLoanFunds(planID, partID); 

и каждая из них делится так:

public void GetParticipantInfo(string planNumber, string participantID) 
    { 
     IAsyncResult _IAsyncResult; 
     List<string> parameter = new List<string>(); 
     parameter.Add(planNumber); 
     parameter.Add(participantID); 
     GetParticipantInfo_A _GetParticipantInfo_A = new GetParticipantInfo_A(GetParticipantInfoAsync); 
     _IAsyncResult = _GetParticipantInfo_A.BeginInvoke(participantID, planNumber, serviceContext, GetParticipantInfoAsyncCallBack, parameter); 

    } 

    public ParticipantDataModel GetParticipantInfoAsync(string planNumber, string partId, ServiceContext esfSC) 
    { 

     ParticipantDataModel pdm = new ParticipantDataModel(); 
     return pdm; 

    } 

    private void GetParticipantInfoAsyncCallBack(IAsyncResult ar) 
    { 
     try 
     { 
      AsyncResult result;    
      result = (AsyncResult)ar; 
      string planID = ((List<string>)ar.AsyncState)[0]; 
      GetParticipantInfo_A caller = (GetParticipantInfo_A)result.AsyncDelegate; 
      ParticipantDataModel pdm = caller.EndInvoke(ar); 
      _cacheManager.SetCache(planID, CacheKeyName.GetPartInfo.ToString(), pdm); 
     } 
     catch (Exception ex) 
     { } 
    } 

Таким образом, вопрос, как настроить поток пользовательского интерфейса, чтобы ждать на звонки, чтобы закончить, прежде чем двигаться дальше к чему-то еще?

В ответ на Джо:

Итак, предполагая, что все они возвращают AsyncResult, я мог сделать что-то вроде:

List<IAsyncResult> results; 
//After each call 
result = OneOfTheAsyncCalls(); 
results.Add(result); 

foreach(IAsyncResult result in results) 
{ 
    result.AsyncWaitHandle.WaitOne(); 
} 

Или это заказ будет дело?

+0

Вы ищете C# 5 – SLaks

ответ

1

Вы можете использовать IAsyncResult.AsyncWaitHandle, чтобы дождаться завершения асинхронной операции.

Ваш пример кода (например, GetParticipantInfo) отбрасывает IAsyncResult. Вместо этого верните его вызывающему.

Смотрите мое редактирование к исходному вопросу

Да ваша правка выглядит, как он будет работать (или вы можете использовать WaitAll, а не зацикливание на WaitOne).

+0

Я изучил это, но не был точно уверен, как его реализовать. Поскольку единственное место, где я действительно имею доступ к результату, находится в асинхронной «ветке», я не знал, что с ним делать. Очевидно, когда вы не используете APM, обратный вызов для асинхронного метода также запускается в рабочем потоке, поэтому я не знаю, где бы я его использовал. Имеет ли это смысл? –

+0

См. Мое редактирование оригинального вопроса –

+0

Работает как очарование! Благодаря! –

3

Можете ли вы использовать AutoResetEvent между вызывающим и обратным вызовом async?

+0

Вы говорите мне: P Я довольно новичок в .NET, поэтому у меня нет огромных знаний здесь –

+0

AutoResetEvent прост в использовании ... http://msdn.microsoft.com/en-us/ library/system.threading.autoresetevent.aspx - позволяет подождать в одной вызывающей функции для ответа в другом асинхронном обратном вызове! Вызовите WaitOne в вызывающей функции с дополнительным тайм-аутом и набором вызовов после получения и обработки ответа! Это позволит вызывающей функции затем перейти от того места, где она ожидала. –

1

Рефакторинг ваших методов, чтобы они использовали метод Task.FromAsync. Например, GetParticipantInfo будет выглядеть так. Я в основном сводил все к этому одному методу.

public Task<ParticipantDataModel> GetParticipantInfo(string planNumber, string participantID) 
{ 
    var instance = new GetParticipantInfo_A(
    (planNumber, partID, esfSC) => 
    { 
     return new ParticipantDataModel(); 
    } 
); 
    var main = Task<ParticipantDataModel>.Factory.FromAsync(
    instance.BeginInvoke, 
    instance.EndInvoke, 
    participantID, 
    planNumber, 
    serviceContext, 
    null); 
    var continuation = main.ContinueWith(
    task => 
    { 
     lock (_cacheManager) 
     { 
     _cacheManager.SetCache(planNumber, CacheKeyName.GetPartInfo.ToString(), task.Result); 
     } 
    }); 
    return continuation; 
} 

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

private void YourButton_Click(object sender, EventArgs args) 
{ 
    var t1 = GetParticipantInfo(partID, plantID); 
    var t2 = SetCurrentInvestments(partID, planID); 
    var t3 = GetLoanFunds(planID, partID); 
    Task.WaitAll(t1, t2, t3); 
} 
+0

Это отличный ответ, +1 наверняка - я бы хотел принять несколько ответов, но в этом специальном случае, блокирующем поток пользовательского интерфейса, на самом деле то, что я хотеть.Я не хочу отображать страницу до тех пор, пока эти вызовы не будут завершены. Единственная причина, по которой они асинхронны, так они будут звонить параллельно и занимать треть времени. –

+0

В веб-приложении вам нужно заблокировать «поток пользовательского интерфейса» (т. Е. Поток, который обрабатывает запрос). Эта реализация (используя «ContinueWith») будет выполнять три задачи последовательно - конечно, OP хочет, чтобы они выполнялись параллельно? – Joe

+0

@PhillipSchmidt: Я неправильно понял ваш вопрос. Я думал, что вы используете приложение Winform или WPF. Я обновил свой ответ, чтобы упростить его. –

Смежные вопросы