2009-09-22 3 views
4

J'ai un service Windows multi-thread assez complexe qui fonctionne, mais je n'arrive pas à comprendre comment nettoyer correctement. Voici un peu de [pseudo] code pour montrer ce que j'ai. Le code actuel est beaucoup plus complexe, probablement trop pour copier/coller ici.Disposer correctement de classe avec thread

Fondamentalement, j'ai une demande de classe qui crée un fil pour faire le travail. Lorsqu'une nouvelle requête arrive dans l'écouteur, elle l'envoie au processeur, ce qui crée la nouvelle demande et maintient la liste des demandes. Si le service est arrêté, je nettoie toutes les demandes de la liste. Mais quand le travail de demande est fait, comment puis-je nettoyer cette instance de la classe?

Merci pour toute aide!

Nelson

class Service 
{ 
    Listener listener; 
    Processor processor; 

    OnStart() 
    { 
    processor = new Processor(); 
    listener = new Listener(processor); 
    } 

    OnStop() 
    { 
    listener.Dispose(); 
    processor.Dispose(); 
    } 
} 

class Listener 
{ 
    Thread thread; 
    bool terminate = false; 

    Listener(Processor processor) 
    { 
    thread = new Thread(DoWork); 
    thread.Start(processor); 
    } 

    DoWork(Processor processor) 
    { 
    WaitForConnection(NewConnection); 
    } 

NewConnection(String data) 
{ 
    processor.NewRequest(data); 

    if (terminate) 
     return; 

    WaitForConnection(NewConnection); 
} 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

class Processor 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    NewRequest(string data) 
    { 
    request.Add(new Request(data)); 
    } 

    Dispose() 
    { 
    //Cleanup each request 
    foreach (Request request in requests) 
    { 
     request.Dispose(); 
    } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    Request(string data) 
    { 
    while (true) 
    { 
     //Do some work 
     Thread.Sleep(1000); 

     if (doneWorking) 
     break; 

     if (terminate) 
     return; 
    } 

    //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
    } 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

Répondre

3

Ceci est une esquisse:

delegate void CompletedRequest(Request req); 

class Processor : ITrackCompletion 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    public void NewRequest(string data) 
    { 
     lock(requests) 
      request.Add(new Request(data), Complete); 
    } 

    public void Complete(Request req) 
    { 
     lock (requests) 
      requests.Remove(req); 
    } 

    public void Dispose() 
    { 
     //Cleanup each request 
     foreach (Request request in requests.ToArray()) 
     { 
      request.Dispose(); 
     } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    public Request(string data, CompletedRequest complete) 
    { 
     try 
     { 
      while (true) 
      { 
       //Do some work 
       Thread.Sleep(1000); 

       if (doneWorking) 
        break; 

       if (terminate) 
        return; 
      } 
     } 
     finally 
     { 
      //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
      complete(this); 
     } 
    } 

    void Dispose() 
    { 
     terminate = true; 
     thread.Join(); 
    } 
} 
+0

C'est essentiellement ce que j'ai maintenant. Merci de confirmer. Pourquoi faites-vous des requêtes.ToArray()? Est-ce que vous pouvez éviter le verrouillage? Est-il plus rapide/plus sûr de quelque façon que ce soit? Merci. –

+0

Plus sûr et plus performant ... Le verrouillage n'est pas possible car l'autre thread le supprimera de ma liste, si je bloque la liste, le Join() ne se terminera jamais. Il est recommandé de ne jamais appeler une fonction pendant qu'un verrou est en attente. De plus, je ne peux pas simplement ignorer le verrou car la collection sera modifiée et ma boucle foreach recevra une exception. –

+0

J'ai eu une expérience de première main à ce sujet sur une autre partie du code. J'ai compris que c'était à cause d'un blocage() blocage, mais grâce à vous j'ai pu le résoudre facilement. –

4

Une possibilité est de passer un rappel à la demande sous la forme d'un délégué: « lorsque vous avez terminé le traitement, appelez-moi pour me dire ». Ensuite, exécutez simplement le rappel à la fin du traitement de la demande et laissez-le gérer le nettoyage. Une chose à surveiller cependant: si vous essayez de parcourir votre liste en éliminant des choses et en essayant de supprimer un élément de la liste dans un autre thread, vous aurez des problèmes. Vous devriez probablement garder un drapeau (accessible d'une manière thread-safe) et une fois que vous avez commencé à disposer de tout dans la liste, ignorer les rappels que vous obtenez.

+0

est le délégué lié à cette instance spécifique du processeur? En d'autres termes, si je crée plusieurs instances de Processor et que je passe un délégué à Request (pour une méthode dans Processor), lorsque j'appelle le délégué, utilise-t-il la même instance avec la bonne liste? Si oui, c'était ce qui me manquait. J'avais la méthode, le délégué, mais je ne pouvais pas (probablement ne devrait pas même si je pourrais) utiliser le délégué directement à travers les classes. –

+0

Sur la sécurité de thread, ne verrouillerait pas simplement() résoudre le problème? Si j'essaie de supprimer un élément de la liste et que Dispose() me bloque, le foreach ne trouve aucun élément. Vice versa, l'article serait retiré et ensuite Dispose() nettoierait le reste. Je vais essayer tout ça et vous le faire savoir. Merci encore. –

+0

Vous auriez besoin de verrouiller toute votre boucle 'foreach' - ce qui ne serait pas vraiment utile, car vous n'en avez plus besoin lorsque le temps est écoulé. Vous pouvez décider exactement à quel point le délégué doit être spécifique - il peut être spécifique à chaque demande individuelle, par exemple. Les méthodes anonymes ou expressions lambda sont susceptibles d'être utiles ici. –

Questions connexes