2017-11-02 26 views
5

Trong ứng dụng của tôi có ba chủ đề như:Đặt giá trị thuộc tính khi tất cả các chuỗi kết thúc?

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

và mỗi thread được bắt đầu theo cách sau:

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

Tôi đã một ListView nơi người dùng có thể chọn một mục và sau đó bắt đầu một lần nữa thread, nhưng tôi muốn ngăn chặn điều này 'vì các phương pháp bên trong mỗi thread là nặng, vì vậy cần thời gian để hoàn thành chúng.

Cho đến bây giờ tôi muốn vô hiệu hóa các lựa chọn ListView, vì vậy tôi đã làm:

<ListView IsEnabled="{Binding IsMatchListEnabled}"> 

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

trước một sự khởi đầu mới Chủ đề tôi làm: IsMatchListEnabled = false; nhưng những gì tôi cần làm là kiểm tra xem tất cả các chủ đề đã kết thúc và sau đó làm: IsMatchListEnabled = true;, trên thực tế nếu tôi bật ListView sau tất cả chuỗi, tôi nhận được ListView thậm chí được bật vì mã Thread không đồng bộ và mã bên ngoài Thread được đồng bộ hóa, vì vậy thực sự thuộc tính này là vô dụng.

Những gì tôi đã cố gắng để tránh điều này là tạo ra một vòng lặp vô hạn như thế này:

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

vòng lặp này được đặt sau khi tất cả chủ đề thực hiện, nhưng khi bạn có thể tưởng tượng, điều này sẽ đóng băng các ứng dụng. Bất kỳ giải pháp nào?

+5

Sử dụng 'Tác vụ thay vì chuỗi. 'Task' cung cấp các cách bình thường để chờ hoàn thành. Bạn _can_ làm điều đó với Threads nếu bạn nhấn mạnh, nhưng không có nhiều lý do để. – Evk

+0

@Evk đúng, cảm ơn gợi ý: – jode

+5

Bạn đã viết mã chuỗi bị cháy và quên. Bạn gần như không bao giờ quên được. Không sử dụng Thread, sử dụng các lớp helper tiện lợi giúp bạn không quên. BackgroundWorker, Task, có lẽ không đồng bộ/đang chờ. Và chắc chắn để đọc về điều này, luồng sẽ ăn bạn sống và nhổ xương của bạn khi bạn không hiểu nó đủ tốt. –

Trả lời

3

Tất cả nhận xét là chính xác - tốt hơn là sử dụng Tasks. Chỉ để trả lời câu hỏi của OP.

Bạn có thể đồng bộ hóa chủ đề với ManualResetEvent, có một loạt sự kiện theo số lượng chủ đề và một chuỗi bổ sung để thay đổi IsMatchListEnabled khi tất cả các chuỗi được hoàn thành.

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 
} 

Sau đó, ở một nơi khác, chúng tôi cần phải khởi tạo thói quen chờ đợi.

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

Ví dụ hoàn chỉnh có thể được tìm thấy và xem here.

1

Tôi khuyên bạn nên sử dụng Khung phản ứng của Microsoft cho việc này. Nó mạnh hơn các nhiệm vụ và mã đơn giản hơn nhiều so với sử dụng các luồng.

Hãy nói rằng bạn có 3 kéo dài hoạt động:

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

Bây giờ bạn có thể viết các truy vấn đơn giản sau đây:

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

Bạn chạy nó như thế này:

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

Kết quả tôi nhận được là:

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

Ba hoạt động 5 giây hoàn tất sau 5.03 giây. Tất cả đều song song.

Nếu bạn muốn dừng tính toán sớm chỉ cần gọi subscription.Dispose().

NuGet "System.Reactive" để nhận bit.

+0

Điều gì sẽ xảy ra trong trường hợp ngoại lệ trong bất kỳ chủ đề nào? Nó sẽ được ném lại gần 'subscription.Subscribe'? – cassandrad

+0

@cassandrad - Không, không phải với mã tôi đã đăng, nhưng có một quá tải khác của phương thức '.Subscribe' sẽ có ngoại lệ nếu bị ném. Bạn cũng có thể đặt các toán tử '.Retry' vào các quan sát để thử lại một số mã không thành công do một ngoại lệ. – Enigmativity

Các vấn đề liên quan