2017-11-02 1 views
5

In meiner Anwendung gibt es drei Themen wie:Eigenschaftswert festlegen, wenn alle Threads beendet sind?

private Thread _analysisThread; 
private Thread _head2HeadThread; 
private Thread _formThread; 

und jeden Thread in der folgenden Art und Weise gestartet:

if (_analysisThread == null || !_analysisThread.IsAlive) 
{ 
    _analysisThread = new Thread(() => { Analysis.Logic(match); }); 
    _analysisThread.Start(); 
} 

ich ein Listview habe, wo der Benutzer ein Element auswählen und dann starten wieder der Thread, aber ich will dies verhindern, weil die Methoden innerhalb jedes Threads schwer sind, also brauchen Sie Zeit, um sie zu vervollständigen.

Bis jetzt will ich die ListView Auswahl deaktivieren, so dass ich tat:

<ListView IsEnabled="{Binding IsMatchListEnabled}"> 

private bool _isMatchListEnabled = true; 
public bool IsMatchListEnabled 
{ 
    get { return _isMatchListEnabled; } 
    set 
    { 
     _isMatchListEnabled = value; 
     OnPropertyChanged(); 
    } 
} 

vor einem neuen Thread Start ich tun: IsMatchListEnabled = false; aber was ich tun muss überprüfen, wenn alle Gewinde fertig sind und dann tun: IsMatchListEnabled = true;, eigentlich, wenn ich die ListView nach allen Thread aktivieren, bekomme ich die ListView sogar aktiviert, weil der Thread Code async ist, und der Code außerhalb der Thread ist sync, so dass diese Eigenschaft tatsächlich nutzlos ist.

Was ich versucht, dies zu vermeiden, ist eine Endlosschleife wie folgt erstellen:

while (true) 
{ 
    if (!_analysisThread.IsAlive && !_head2HeadThread.IsAlive && !_formThread.IsAlive) 
    { 
      IsMatchListEnabled = true; 
      break; 
    } 
} 

diese Schleife, nachdem alle Fäden Ausführung gelegt wird, sondern wie man sich vorstellen kann, wird dies die Anwendung einfrieren. Irgendeine Lösung?

+5

Verwenden Sie 'Task' statt Threads. "Task" bietet normale Möglichkeiten, auf den Abschluss zu warten. Du kannst das mit Threads machen, wenn du darauf bestehst, aber es gibt keinen Grund dazu. – Evk

+0

@Evk das ist richtig, danke für den Hinweis: – jode

+5

Sie schrieb Fire-and-Forgot-Thread-Code. Das kannst du fast nie vergessen. Verwenden Sie nicht Thread, verwenden Sie die praktischen Hilfsklassen, die Ihnen helfen, nicht zu vergessen. BackgroundWorker, Aufgabe, vielleicht async/warten. Und achten Sie darauf, dies zu lesen, wird Threading essen Sie am Leben und spucken Sie Ihre Knochen aus, wenn Sie es nicht gut genug verstehen. –

Antwort

3

Alle Kommentare sind korrekt - es ist besser zu verwenden Tasks. Nur um die Frage von OP zu beantworten.

Sie können Threads mit ManualResetEvent synchronisieren, mit einem Array von Ereignissen nach der Anzahl der Threads und einem zusätzlichen Thread, um IsMatchListEnabled zu ändern, wenn alle Threads beendet sind.

public static void SomeThreadAction(object id) 
{ 
    var ev = new ManualResetEvent(false); 
    events[id] = ev; // store the event somewhere 

    Thread.Sleep(2000 * (int)id); // do your work 

    ev.Set(); // set the event signaled 
} 

Dann woanders müssen wir warten Routine zu initialisieren.

// we need tokens to be able to cancel waiting 
var cts = new CancellationTokenSource(); 
var ct = cts.Token; 

Task.Factory.StartNew(() => 
{ 
    bool completed = false; 
    while (!ct.IsCancellationRequested && !completed) 
    { 
     // will check if our routine is cancelled each second 
     completed = 
      WaitHandle.WaitAll(
       events.Values.Cast<ManualResetEvent>().ToArray(), 
       TimeSpan.FromSeconds(1)); 
    } 

    if (completed) // if not completed, then somebody cancelled our routine 
     ; // change your variable here 
}); 

Komplettes Beispiel kann gefunden und angesehen werden here.

1

Ich würde vorschlagen, Microsoft Reactive Framework dafür zu verwenden. Es ist leistungsfähiger als Aufgaben und der Code ist viel einfacher als die Verwendung von Threads.

Angenommen, Sie haben 3 lang laufende Operationen:

Action huey =() => { Console.WriteLine("Huey Start"); Thread.Sleep(5000); Console.WriteLine("Huey Done"); }; 
Action dewey =() => { Console.WriteLine("Dewey Start"); Thread.Sleep(5000); Console.WriteLine("Dewey Done"); }; 
Action louie =() => { Console.WriteLine("Louie Start"); Thread.Sleep(5000); Console.WriteLine("Louie Done"); }; 

Jetzt können Sie die folgende einfache Abfrage schreiben:

IObservable<Unit> query = 
    from a in new [] { huey, dewey, louie }.ToObservable() 
    from u in Observable.Start(() => a()) 
    select u; 

Sie führen Sie es wie folgt aus:

Stopwatch sw = Stopwatch.StartNew(); 
IDisposable subscription = query.Subscribe(u => { },() => 
{ 
    Console.WriteLine("All Done in {0} seconds.", sw.Elapsed.TotalSeconds); 
}); 

Die Ergebnisse, die ich bekomme, sind:

 
Huey Start 
Dewey Start 
Louie Start 
Huey Done 
Louie Done 
Dewey Done 
All Done in 5.0259197 seconds. 

Drei 5-Sekunden-Operationen in 5,03 Sekunden abgeschlossen. Alles parallel.

Wenn Sie die Berechnung früh stoppen wollen, rufen Sie einfach subscription.Dispose().

NuGet "System.Reactive", um die Bits zu erhalten.

+0

Was passiert im Falle einer Ausnahme in einem der Threads? Wird es in der Nähe von 'subscription.Subscribe' zurückgeworfen? – cassandrad

+0

@cassandrad - Nein, nicht mit dem Code, den ich gepostet habe, aber es gibt eine andere Überladung der '.Subscribe' Methode, die eine Exception auslöst, wenn eine geworfen wird. Sie können auch '.Retry'-Operatoren auf Observables setzen, um Code, der aufgrund einer Ausnahme fehlschlägt, erneut zu versuchen. – Enigmativity

Verwandte Themen