2009-05-11 6 views
2

J'ai le code suivant dans une bibliothèque de classes. Et j'attends un rappel dans mon application principale. Je fais un appel DownloadStringAsync donc je dois attendre quelques secondes pour obtenir le rappel après qu'il a fini. J'ai un 3 de ces appels à attendre, donc dans mon application principale j'utilise AutoResetEvent pour attendre tous pour finir. Donc, je vais bloquer jusqu'à ce qu'ils aient été définis dans la fonction de rappel. Cependant, après avoir testé le rappel, vous ne serez pas appelé. Je pense quand le code est bloqué par le AutoResetEvent son blocage du DownloadStringAsync. Comme quand je commente ce code tout fonctionne bien.C# Filetage et utilisation de AutoResetEvent

Donc, je pense dès que je fais un appel à: objNoGateway.NoGatewayStatus (sipUsername, statusDisplay1.PhoneNumber); Et quand le code atteint ici: handle.WaitOne(); Il va bloquer le code dans la bibliothèque de classes.

Merci beaucoup pour tout conseil.

Dans mon exemple de code de bibliothèque de classes.

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

Dans mon application principale, j'inscris ce rappel. Et attendez le pour le résultat. Ensuite, définissez l'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(); 
    } 

La partie lorsque j'appelle et bloque.

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");  

======================== Modifier et a ajouté les 2 autres callbacks ne pas confondre la question de moi juste avoir une seule ======================

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

Selon votre code, vous devriez lire sur la console "events.WaitOne():" + handle.ToString() exactement une fois avant de pendre. Vous n'en avez pas parlé dans votre message, mais c'est un indice précieux. Alors, est-ce arrivé? – DonkeyMaster

+0

Cette ligne n'est jamais atteinte: Console.WriteLine ("events.WaitOne():" + handle.ToString()); – ant2009

+0

Si je devais déboguer cela, je serais passé par le débogueur. Pouvez-vous nous dire quelles lignes de code s'exécutent et lesquelles ne le font pas? Les rappels sont-ils en cours d'exécution? La méthode wc_DownloadStringCompleted est-elle exécutée? Dans votre code, vous n'avez pas montré où les méthodes OnValidTelephoneNumberCompleted et OnCalledNumberBlockedCompleted sont appelées. En outre, cela m'irrite: public EventHandler NoGatewayCompletedEvent; et plus loin ... NoGatewayCompletedEvent (this, e); Eh bien, cela ne devrait pas compiler, vous n'avez pas écrit le mot-clé "événement". Mais il n'a probablement rien à faire. – DonkeyMaster

Répondre

1

Est-ce aussi simple que: vous appelez toujours situé sur l'index 0?

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

En fait, j'ai 3 rappels différents. Pour être simple je n'en ai montré qu'un ici. Les autres rappels régleront 1 et 2. J'ai changé pour ManualResetEvent. Cependant le problème reste. Si je commente cette ligne handle.WaitOne(); Mon rappel est reçu. Cependant, quand je ne commente pas, il continue à bloquer et à geler mon interface utilisateur. Merci pour plus de suggestions. – ant2009

+0

Je pense que quelque chose comme ça est utilisé pour appeler un événement terminé.AsyncOp.PostOperationCompleted (this.onCompletedDelegate, args) ; Lorsqu'un thread asynchrone utilise PostOperationComplete pour appeler un délégué terminé, il utilise le contexte de thread de la classe d'appelant. Si vous avez appelé WaitOne sur le contexte du thread appelant après avoir démarré le thread asynchrone, il ne pourra pas l'exécuter car le thread de la classe appelante est bloqué. – Taliesin

0

Après beaucoup de modifications, je pense que je pourrais comprendre le problème. Les applications Windows Forms ont un thread principal; ce fil est utilisé pour traiter les messages. Ainsi, lorsque votre thread principal bloque, votre application ne peut pas recevoir d'événements. Et vous utilisez WaitOne pour bloquer le thread principal.

Je déplace les vérifications WaitOne() vers un thread de minuteur distinct.

Ou vous pouvez attendre un temps limité, et demander l'application de traiter les messages entre:

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

L'approche plus tard n'est pas quelque chose que vous devez faire dans une bibliothèque bien. C'est quelque chose d'anti-pattern je pense.

+0

J'ai ajouté les 2 rappels restants. Ordinaire, je ne les ai pas montrés car cela rendait le code trop long à poster. – ant2009

+0

Cette ligne n'est jamais atteinte. Donc, n'écrit jamais à la console. Console.WriteLine ("events.WaitOne():" + handle.ToString()); – ant2009

+0

OnNoGatewayCompleted n'est jamais atteint. Le waitOne() bloque déjà avant que le rappel ait une chance de rappeler. – ant2009

0

Le fragment de code est propre

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

Toutefois, dans le 2ème dernier extrait, associez un gestionnaire d'événements pour cet événement comme suit .. OnNoGatewayCompleted semble être une méthode d'assistance pour déclencher l'événement .. (il ne devrait pas être public) mais ici, il semble que le gestionnaire d'événement relance l'événement.Sauf si vous avez 2 méthodes nommées OnNoGatewayCompleted (J'espère pas)

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

Si vous cherchez les waitHandles à signaler dans le gestionnaire d'événements, ne doivent pas les méthodes OnCalledNumberBlockedCompleted être accroché à l'événement au lieu .

PS: Comme Marc a souligné .. utiliser WaitHandle.WaitAll (la demande pour la boucle que les opérations de async complète pour ce qui ne peut pas être le cas)

+0

Ne pense pas que la boucle exige qu'ils se complètent dans l'ordre? – Andomar

+0

Le OnCalledNumberBlockedCompleted est accroché, seulement je ne l'ai pas montré, car il rend le message beaucoup plus long. Je viens de démontrer avec juste le gestionnaire d'événement NoGateway. – ant2009

-3

utilisation WaitHandle.WaitAny (handleArray); attendre sur toutes les poignées dans le tableau handle au lieu de handle.WaitOne(); dans une boucle

+1

-1 si WaitOne bloque, WaitAll bloquera encore plus dur – Andomar

+0

Yup .. vous avez raison ... je ne sais pas ce que je pensais !!! –

+0

il ne sera pas plus difficile si ... sera le même, sauf w/o une boucle –

1

Essayez quelque chose le long de ces lignes:

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

Est-ce le faire?

+0

Je pense que je comprends ce que vous voulez dire. Je vais essayer de vous le faire savoir. – ant2009