2012-04-03 14 views
1

Dies kann eine dumme Frage sein und wenn dies bereits an anderer Stelle beantwortet wurde, dann würde ich es wirklich schätzen, wenn jemand mich darauf hinweisen könnte, da meine Suche nicht aufgetaucht ist etwas Definitives.Thread.Join im UI-Thread blockiert auch Kind-Thread


Auf den Punkt gebracht, mein Problem ist, dass, wenn ich childThread.Join() im UI-Thread auf einem untergeordneten Thread zu tun, die die childThread zu stoppen markiert wurde scheint sowie dem Haupt-Thread zu blockieren, so dass alles hängt einfach.
Dass die Benutzeroberfläche aufgrund der Verwendung von Join blockiert wird, ist im Moment kein Problem, da das childThread in weniger als einer Sekunde enden sollte, nachdem es sowieso beendet wurde.
Dies passiert, während ich darauf warte, dass ein Thread, der einen wiederholten Prozess ausführt, beendet wird, bevor ich eine andere Methode ausführen kann, die einige Informationen zurückgibt, aber nicht gleichzeitig mit dem anderen Prozess ausgeführt werden kann.

Meine Winforms-Anwendung integriert sich mit einem Stück USB-Hardware, indem Sie die C-API für die Hardware pinvokieren.

Die Hardware-API verfügt über eine Methode, die einen Prozess startet, der unbegrenzt und wiederholt ausgeführt wird und schnell mit neuen Informationen zurückruft, die ich dann an die Benutzeroberfläche weiterleiten muss.
Diese Operation kann durch einen anderen Aufruf der Hardware-API abgebrochen werden, die ein Flag setzt, das die Hardware sehen kann, damit sie es beenden kann.
Ich habe diese C-API mit meinem eigenen C# -Code umschlossen, und innerhalb des Wrappers musste ich den Aufruf des Startprozesses in einem anderen Thread aufheben, damit die Aktivität die Benutzeroberfläche nicht blockiert.

Hier sind die bearbeiteten Highlights von grob, was ich mache.

public class DeviceWrapper 
{ 
    Thread childThread = null; 

    void DeviceWrapper 
    { 
     //Set the callback to be used by the StartGettingInformation() process 
     PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback); 
    } 

    public void StartProcess() 
    { 
     childThread = new Thread(new ThreadStart(GetInformationProcess)) 
     childThread.Start(); 
    } 

    void GetInformationProcess() 
    { 
     PInvokeMethods.StartGettingInformation(); 
    } 

    //This callback occurs inside the childThread 
    void InformationAcquiredCallback(Status status, IntPtr information) 
    { 
     //This callback is triggered when anything happens in the 
     //StartGettingInformation() method, such as when the information 
     //is ready to be retrieved, or when the process has been cancelled. 
     if(status == Status.InformationAcquired) 
     { 
      FireUpdateUIEvent(); 
     } 
     //If the cancel flag has been set to true this will be hit. 
     else if(status == Status.Cancelled) 
     { 
      //Reset the cancel flag so the next operation works ok 
      PInvokeMethods.SetCancelFlag(false); 

      childThread.Abort(); 
     } 
    } 

    //This method runs once, and can't run at the same time as GetInformationProcess 
    public string GetSpecificInformation() 
    { 
     //This triggers InformationAcquiredCallback with a status of Cancelled 
     StopProcess(); 

     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 

     return PInvokeMethods.GetSpecificInformation(); 
    } 

    public void StopProcess() 
    { 
     PInvokeMethods.SetCancelFlag(true); 
    } 
} 

diesen Code verwenden, wenn ich rufe childThread.Join() die gesamte Anwendung zum Stillstand kommt (was ich für die Benutzeroberfläche erwarten würde und das ist in Ordnung) und der childThread auch, weil der Rückruf wird nie zu stoppen scheinen wieder getroffen.

Allerdings, wenn ich den folgenden Code statt:

public string GetSpecificInformation() 
{ 
    //This triggers InformationAcquiredCallback with a status of Cancelled 
    StopProcess(); 
    string s = ""; 

    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate 
    { 
     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 
     s = PInvokeMethods.GetSpecificInformation();    
    })); 

    return s; 
} 

Dann wird alles getroffen als erwartet und childThread tut beenden und alles ist gut, außer offensichtlich meine Zeichenfolge vor dem WaitCallback Feuer leer zurückgegeben werden und ordnen es.

Also, muss ich nur saugen und ändern Sie die Klasse, so dass ich die QueueUserWorkItem und WaitCallback und feuern ein Ereignis, um mit meiner String-Rückgabe umzugehen?
Gibt es etwas Dummes, das ich in meinem ersten Ansatz mache, was dazu führt, dass der ChildThread ebenfalls blockiert?
Oder gibt es eine andere Taktik oder Klasse, die ich verwenden sollte, wenn man bedenkt, dass es .NET 3.5 ist?

+3

Ich habe die letzten 30 Jahre damit verbracht, zu versuchen, Delphi-Entwickler mit TThread.WaitFor() und anderen solchen Hard-Lock-Synchronisationsmechanismen zu stoppen, die Deadlocks generieren. Gerade wenn ich denke, dass ich irgendwo hinkomme, entdecken Java und C# Entwickler join(). Ein nie endender Albtraum. –

+0

Glauben Sie mir, ich würde nichts davon berühren, wenn ich nicht verzweifelt und aus meiner Tiefe wäre;) Was würden Sie stattdessen empfehlen? – Nanhydrin

+0

Wenn Sie Eric und andere Re hören. Aufrufen, BeginInvoke usw., Sie werden nicht weit falsch liegen. Warte nicht in Event-Handlern - handle auf Signale, die zurückgegeben werden, indem Delegierte auf den Haupt-Thread geschossen werden, wenn die anderen Threads etwas zu sagen haben. –

Antwort

5

Nun, klingt FireUpdateUIEvent(); wie ein Verfahren, das könnte Beitrag zum MsgQueue senden (Control.Invoke()). Wenn der Haupt-Thread in einem Join() wartet, dann haben Sie einen klassischen Deadlock.

Darüber hinaus gilt childThread.Abort() nicht als sicher.

Also, muss ich nur saugen und ändern Sie die Klasse, so dass ich die QueueUserWorkItem und WaitCallback und feuern ein Ereignis, um mit meiner Zeichenfolge zurück zu gehen?

Ich würde es sicherlich neu gestalten. Es kann wahrscheinlich ein bisschen vereinfacht werden.

+0

FireUpdateUIEvent aktualisiert nicht direkt die Benutzeroberfläche selbst, aber ein Handler würde auf jeden Fall von der Benutzeroberfläche zur Verfügung gestellt werden, und dieser Rückruf blockiert die Wartezeit auf die Rückkehr. Aber da es nicht wirklich einen Wert zurückgibt oder irgendetwas, das mir wichtig ist, könnte ich es vermutlich in einen anderen Thread übersetzen, oder gibt es etwas Besseres, das ich tun sollte? – Nanhydrin

+0

@ Nanhydrin: Henk hat Recht; Du solltest das Ganze neu gestalten. Sie sollten weder einen Thread-Abbruch noch einen Join vom UI-Thread ausführen. Wenn Sie dem UI-Thread mitteilen möchten, dass etwas Interessantes in einem anderen Thread passiert ist, führen Sie die gleichen Aktionen aus, die das Betriebssystem ausführt: Fügen Sie eine Nachricht in die Warteschlange ein. –

+0

FireUpdateUIEvent sollte Control.BeginInvoke und nicht Control.Invoke verwenden. Das könnte den Stillstand lösen. Aber Sie hätten immer noch die Join() und die Abort(), keine gute Situation. –

Verwandte Themen