2009-05-11 8 views
2

У меня есть следующий код в библиотеке классов. И я жду вызова в основное приложение. Я делаю вызов DownloadStringAsync, поэтому мне нужно подождать несколько секунд, чтобы получить обратный вызов после его завершения. У меня есть 3 из этих вызовов, чтобы подождать, поэтому в моем основном приложении я использую AutoResetEvent, чтобы подождать, пока все они закончатся. Поэтому я заблокирую, пока они не будут установлены в функции обратного вызова.C# Threading и использование AutoResetEvent

Однако после тестирования обратный вызов не вызывается. Я думаю, когда код блокируется приложением AutoResetEvent, блокирующим DownloadStringAsync. Когда я комментирую этот код, все работает нормально.

Так что я думаю, как только я позвоню: objNoGateway.NoGatewayStatus (sipUsername, statusDisplay1.PhoneNumber); И когда код достигает здесь: handle.WaitOne(); Он заблокирует код в библиотеке классов.

Большое спасибо за любой совет.

В моем классе образец кода библиотеки.

 // Event handler that makes a call back in my main application 
    // Event handler and method that handles the event 
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent; 
    // The method that raises the event. 
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e) 
    { 
     if (NoGatewayCompletedEvent != null) 
     { 
      NoGatewayCompletedEvent(this, e); 
     } 
    } 

    // Start the Async call to find if NoGateway is true or false 
    public void NoGatewayStatus(string sipUsername, string phoneNumber) 
    {  
     string strURL = string.Format("http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber); 

     if (!wc.IsBusy) 
     { 
      try 
      { 
       string errorMsg = string.Empty; 
       wc.DownloadStringAsync(new Uri(strURL)); 
      } 
      catch (WebException ex) 
      { 
       Console.WriteLine("IsNoGateway: " + ex.Message); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("IsNoGateway: " + ex.Message); 
      } 
     } 
     else 
     { 
      Console.WriteLine("WebClient: IsNoGateWay(): Busy please try again"); 
     } 

    } 

    void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) 
    { 
     if (e.Error == null) 
     { 
      if (e.Result == "No gateway") 
      { 
       OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.VALIDATION_FAILED)); 
       Console.WriteLine("NoGatway() DownloadedCompleted: " + e.Result); 
      } 
      else 
      { 
       OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.OK)); 
       Console.WriteLine("NoGateway() DownloadCompleted: " + e.Result); 
      } 
     } 
     else 
     { 
      this.OnNoGatewayCompleted(this, new NoGatewayEventArgs(validateResponse_e.SERVER_FAILED)); 
      Console.WriteLine("No Gateway: DownloadCompleted() Error: " + e.Error.Message); 
     } 
    } 

В моем основном приложении я регистрирую этот обратный вызов. И ждите результата. Затем установите AutoResetEvent.

ManualResetEvent[] waitValidateCallResponse = new ManualResetEvent[] 
      { new ManualResetEvent(false), new ManualResetEvent(false), new ManualResetEvent(false) }; 
    // Event handler for NoGateway event 
    private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e) 
    { 
     Console.WriteLine("OnNoGatewayComleted: " + e.noGateway); 
     waitValidateCallResponse[0].Set(); 
    } 

Часть, когда я звоню и блокирую.

NoGateway objNoGateway = new NoGateway()   
objNoGateway.NoGatewayCompletedEvent += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted); 
objNoGateway.NoGatewayStatus(sipUsername, statusDisplay1.PhoneNumber); 


// Block here - Wait for all reponses to finish before moving on 
waitEvent.WaitOne(5000, true);      
Console.WriteLine("All thread finished");  

======================== Редактировать и добавил 2 других обратных вызовов, чтобы не запутать вопрос меня просто имеющий только один ======================

private void OnCalledNumberBlockedCompleted(object sender, CalledNumberBlockedEventArgs e) 
    { 
     Console.WriteLine("OnCalledNumberBlockedCompleted: " + e.CalledNumberBlocked); 
     waitValidateCallResponse[1].Set(); 
    } 

    private void OnValidTelephoneNumberCompleted(object sender, ValidTelephoneNumberEventArgs e) 
    { 
     Console.WriteLine("OnValidTelephoneNumberCompleted: " + e.validTelephoneNumber); 
     waitValidateCallResponse[2].Set(); 
    } 
+0

Согласно вашему коду, вы должны прочитать на консоли «events.WaitOne():« + handle.ToString() ровно один раз перед тем, как повесить. Вы не говорили об этом в своем посте, но это ценная подсказка. Итак, это случилось? – DonkeyMaster

+0

Эта строка никогда не достигается: Console.WriteLine ("events.WaitOne():" + handle.ToString()); – ant2009

+0

Если бы мне пришлось отлаживать это, я бы прошел через отладчик. Можете ли вы сказать нам, какие строки кода выполняются, а какие нет? Выполняются ли обратные вызовы? Выполняется ли метод wc_DownloadStringCompleted? В вашем коде вы не указали, где вызываются методы OnValidTelephoneNumberCompleted и OnCalledNumberBlockedCompleted. Кроме того, это раздражает меня: общественный EventHandler NoGatewayCompletedEvent; и дальше ... NoGatewayCompletedEvent (это, e); Ну, это не должно компилироваться, вы не пишете ключевое слово «событие». Но, вероятно, нечего делать. – DonkeyMaster

ответ

1

Является ли это так просто, как вы всегда называют Установить по индексу 0?

private void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e) 
{ 
    Console.WriteLine("OnNoGatewayComleted: " + e.noGateway); 
    waitValidateCallResponse[0].Set(); 
} 
+0

На самом деле у меня есть 3 разных обратных вызова. Чтобы быть простым, я отобразил только один из них. Другие обратные вызовы будут установлены 1 и 2. Я перешел на ManualResetEvent. Однако проблема остается. Если я прокомментирую эту строку handle.WaitOne(); Мой обратный вызов получен. Однако, когда я раскомментирую, он продолжает блокировать и замораживать мой пользовательский интерфейс. Спасибо за любые предложения. – ant2009

+0

Я подозреваю, что что-то вроде этого используется для вызова завершенного события. asyncOp.PostOperationCompleted (this.onCompletedDelegate, args); Когда асинхронный поток использует PostOperationComplete для вызова завершенного делегата, он использует контекст потока вызывающего класса. Если вы вызвали WaitOne в контексте вызывающего потока после запуска асинхронного потока, он не сможет выполнить его, поскольку поток вызывающего класса заблокирован. – Taliesin

0

После многих изменений, я думаю, я мог бы понять проблему. Приложения Windows Forms имеют один основной поток; этот поток используется для обработки сообщений. Поэтому, когда основной поток блокируется, ваше приложение не может получать события. И вы используете WaitOne, чтобы блокировать основной поток.

Я бы перенес проверку WaitOne() на отдельный поток таймера.

Или вы могли бы ждать в течение ограниченного времени, и проинструктировать приложение для обработки сообщений между:

foreach (WaitHandle handle in waitValidateCallResponse) 
{ 
    while (!handle.WaitOne(300)) 
     Application.ProcessMessages(); 
    Console.WriteLine("events.WaitOne(): " + handle.ToString()); 
} 

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

+0

Я добавил 2 оставшихся обратного вызова. Обычный, я не показывал их, так как он слишком долго писал код. – ant2009

+0

Эта линия никогда не достигается. Поэтому никогда не записывайте на консоль. Console.WriteLine ("events.WaitOne():" + handle.ToString()); – ant2009

+0

OnNoGatewayCompleted так и не был достигнут. Функция waitOne() уже блокирует до того, как обратный вызов имеет возможность обратного вызова. – ant2009

0

фрагмент кода свойственен

// Event handler that makes a call back in my main application 
    // Event handler and method that handles the event 
    public EventHandler<NoGatewayEventArgs> NoGatewayCompletedEvent; 
    // The method that raises the event. 
    public void OnNoGatewayCompleted(object sender, NoGatewayEventArgs e) 
    { 
     if (NoGatewayCompletedEvent != null) 
     { 
      NoGatewayCompletedEvent(this, e); 
     } 
    } 

Однако во 2-ом последнем фрагменте, вы подключаете обработчик события для этого события следующим образом .. OnNoGatewayCompleted кажется вспомогательный метод, чтобы поднять событие .. (это не должно быть общедоступным), но здесь, похоже, у вас есть обработчик событий, который снова поднимает событие.Если у вас есть 2 метода с именем OnNoGatewayCompleted (я не надеясь)

objNoGateway.NoGatewayCompletedEvent 
    += new EventHandler<NoGatewayEventArgs>(this.OnNoGatewayCompleted); 

Если вы ищете waitHandles, чтобы сигнализировать в обработчик событий, не должны методы OnCalledNumberBlockedCompleted быть подключены к событию вместо ,

PS: Как Марк указал .. использовать WaitHandle.WaitAll (для цикла требует, чтобы операции завершения асинхронных в порядке, который не может быть случай)

+0

Не думайте, что цикл требует, чтобы они были закончены? – Andomar

+0

OnCalledNumberBlockedCompleted подключен, только я не показывал его, так как он делает сообщение намного дольше. Я просто продемонстрировал только обработчик событий NoGateway. – ant2009

-3

Использование WaitHandle.WaitAny (handleArray); дождаться всех ручек в массиве дескрипторов вместо handle.WaitOne(); в цикле

+1

-1 если WaitOne блокирует, WaitAll будет блокировать еще сложнее – Andomar

+0

Yup .. вы правы ... я не знаю, что я думал! –

+0

это не будет сложнее, хотя ... будет таким же, кроме петли –

1

Попробуйте что-то вдоль этих линий:

public void NoGatewayStatus (string sipUsername, string phoneNumber) { 
    string strURL = string.Format("http://xxxxxxxxxxxxxxx={0}&CalledNumber={1}", sipUsername, phoneNumber); 

    ManualResetEvent wait1 = new ManualResetEvent(false); 
    WebClient wc = new WebClient(); 
    Thread thr = new Thread(DownloadSomeStuff); 
    thr.Start(new DlArguments(strURL, wait1)); 

    // do the other three 

    if (!wait1.WaitOne(10000)) { 
     Console.WriteLine("DownloadSomeStuff timed out"); 
     return; 
    } 
    if (!wait2.WaitOne(10000)) { 
     Console.WriteLine("DownloadOtherStuff timed out"); 
     return; 
    } 
    if (!wait3.WaitOne(10000)) { 
     Console.WriteLine("DownloadMoreStuff timed out"); 
     return; 
    } 
} 

public void DownloadSomeStuff (object p_args) { 
    DlArguments args = (DlArguments) p_args; 
    try { 
     WebClient wc = new WebClient(); 
     wc.DownloadString(args.Url); 
     args.WaitHandle.Set(); 
    } catch (Exception) { 
     // boring stuff 
    } 
} 


private class DlArguments 
{ 
    public DlArguments (string url, ManualResetEvent wait_handle) { 
     this.Url = url; 
     this.WaitHandle = wait_handle; 
    } 

    public string Url { get; set; } 
    public ManualResetEvent WaitHandle { get; set; } 
} 

ли это делать?

+0

Думаю, я понимаю, что вы имеете в виду. Я попробую и дам вам знать. – ant2009

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