2008-09-27 25 views
32

Tôi đã làm việc trên ứng dụng web thu thập dữ liệu .NET trong thời gian rảnh và một trong các tính năng của ứng dụng này mà tôi muốn đưa vào là nút tạm dừng để tạm dừng một chuỗi cụ thể.Có cách nào để tạm dừng vô hạn một luồng không?

Tôi tương đối mới đối với đa luồng và tôi không thể tìm ra cách tạm dừng một chuỗi vô hạn định hiện đang được hỗ trợ. Tôi không thể nhớ chính xác lớp/phương pháp, nhưng tôi biết có một cách để làm điều này nhưng nó đã được gắn cờ là lỗi thời bởi các.NET framework.

Có cách nào có mục đích chung tốt để tạm dừng vô hạn một chuỗi công nhân trong C# .NET.

Tôi chưa có nhiều thời gian gần đây để làm việc trên ứng dụng này và lần cuối tôi chạm vào trong khung công tác .NET 2.0. Tôi mở cho bất kỳ tính năng mới nào (nếu có) tồn tại trong khung công tác .NET 3.5, nhưng tôi muốn biết giải pháp cũng hoạt động trong khung 2.0 vì đó là những gì tôi sử dụng tại nơi làm việc và sẽ tốt biết chỉ trong trường hợp.

Trả lời

90

Không bao giờ, sử dụng Thread.Suspend. Vấn đề chính với nó là 99% thời gian bạn không thể biết những gì mà thread đang làm khi bạn đình chỉ nó. Nếu chủ đề đó giữ khóa, bạn sẽ dễ dàng xâm nhập vào tình huống bế tắc, v.v. Hãy nhớ rằng mã bạn đang gọi có thể có được/giải phóng khóa đằng sau hậu trường. Win32 có một API tương tự: SuspendThreadResumeThread. Các tài liệu sau đây cho SuspendThread cho một bản tóm tắt tốt đẹp của sự nguy hiểm của các API:

http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx

Chức năng này được thiết kế chủ yếu để sử dụng bởi bộ dò lỗi. Nó không được dùng để đồng bộ hóa luồng. Gọi SuspendThread trên một chủ sở hữu một đối tượng đồng bộ hóa, chẳng hạn như một mutex hoặc phần quan trọng, có thể dẫn đến bế tắc nếu chuỗi cuộc gọi cố gắng lấy một đối tượng đồng bộ thuộc sở hữu của một chuỗi bị treo. Để tránh tình trạng này, một luồng trong một ứng dụng không phải là trình gỡ lỗi sẽ báo hiệu luồng khác để treo chính nó. Chuỗi đích phải được thiết kế để theo dõi tín hiệu này và phản hồi một cách thích hợp.

Cách thích hợp để tạm dừng một chuỗi vô thời hạn là sử dụng ManualResetEvent. Chủ đề có nhiều khả năng lặp, thực hiện một số công việc. Cách dễ nhất để đình chỉ các chủ đề là phải có các chủ đề "kiểm tra" sự kiện này mỗi lần lặp, như vậy:

while (true) 
{ 
    _suspendEvent.WaitOne(Timeout.Infinite); 

    // Do some work... 
} 

Bạn chỉ định một thời gian chờ vô hạn nên khi sự kiện này không hiệu, thread sẽ chặn vô thời hạn, cho đến khi sự kiện này được báo hiệu tại thời điểm mà luồng sẽ tiếp tục tại nơi nó đã dừng lại.

Bạn sẽ tạo ra sự kiện này như sau:

ManualResetEvent _suspendEvent = new ManualResetEvent(true); 

Tham số true nói sự kiện này để bắt đầu ra ở trạng thái báo hiệu.

Khi bạn muốn tạm dừng thread, bạn thực hiện như sau:

_suspendEvent.Reset(); 

Và để tiếp tục các chủ đề:

_suspendEvent.Set(); 

Bạn có thể sử dụng một cơ chế tương tự như tín hiệu thread để thoát ra và chờ đợi cả hai sự kiện, phát hiện sự kiện nào được báo hiệu.

Just for fun tôi sẽ cung cấp một ví dụ hoàn chỉnh:

public class Worker 
{ 
    ManualResetEvent _shutdownEvent = new ManualResetEvent(false); 
    ManualResetEvent _pauseEvent = new ManualResetEvent(true); 
    Thread _thread; 

    public Worker() { } 

    public void Start() 
    { 
     _thread = new Thread(DoWork); 
     _thread.Start(); 
    } 

    public void Pause() 
    { 
     _pauseEvent.Reset(); 
    } 

    public void Resume() 
    { 
     _pauseEvent.Set(); 
    } 

    public void Stop() 
    { 
     // Signal the shutdown event 
     _shutdownEvent.Set(); 

     // Make sure to resume any paused threads 
     _pauseEvent.Set(); 

     // Wait for the thread to exit 
     _thread.Join(); 
    } 

    public void DoWork() 
    { 
     while (true) 
     { 
      _pauseEvent.WaitOne(Timeout.Infinite); 

      if (_shutdownEvent.WaitOne(0)) 
       break; 

      // Do the work here.. 
     } 
    } 
} 
15

Các Threading in C# ebook tóm tắt Thread.Suspend và Thread.Resume thusly:

Các Suspend và Resume phương pháp phản đối có hai chế độ - nguy hiểm và vô ích!

Cuốn sách đề nghị sử dụng một cấu trúc đồng bộ như một AutoResetEvent hoặc Monitor.Wait để thực hiện chủ đề đình chỉ và khôi phục.

+0

tôi sẽ phải đi và đọc ebook đó. Tôi chưa bao giờ gặp sự cố với việc Tạm dừng/Tiếp tục lại. –

0

Bên cạnh gợi ý ở trên, tôi muốn thêm một tip. Trong một số trường hợp, sử dụng BackgroundWorker có thể đơn giản hóa mã của bạn (đặc biệt là khi bạn sử dụng phương pháp ẩn danh để xác định DoWork và các sự kiện khác của nó).

0

Phù hợp với những gì người khác nói - đừng làm điều đó. Những gì bạn thực sự muốn làm là để "tạm dừng công việc", và để cho chủ đề của bạn đi lang thang miễn phí. Bạn có thể cho chúng tôi biết thêm chi tiết về (các) chủ đề bạn muốn tạm ngừng không? Nếu bạn không bắt đầu chủ đề, bạn chắc chắn không nên xem xét việc đình chỉ nó - nó không phải của bạn. Nếu đó là chủ đề của bạn, sau đó tôi đề nghị thay vì đình chỉ nó, bạn chỉ cần có nó ngồi, chờ đợi cho công việc nhiều hơn để làm. Brannon có một số gợi ý tuyệt vời cho tùy chọn này trong phản ứng của anh ta. Ngoài ra, hãy để nó kết thúc; và quay lên một cái mới khi bạn cần.

-1

Tạm dừng() và Tiếp tục() có thể bị xóa, tuy nhiên chúng không hề vô dụng. Nếu, ví dụ, bạn có một luồng làm một dữ liệu thay đổi công việc kéo dài và người dùng muốn dừng nó, anh ta nhấn vào một nút. Tất nhiên, bạn cần yêu cầu xác minh, nhưng, đồng thời bạn không muốn chuỗi đó tiếp tục thay đổi dữ liệu, nếu người dùng quyết định rằng anh ấy thực sự muốn hủy bỏ. Tạm dừng Chủ đề trong khi chờ người dùng nhấp vào nút Có hoặc Không trong hộp thoại xác nhận là cách chỉ để ngăn không cho thay đổi dữ liệu trước khi bạn báo hiệu sự kiện hủy bỏ được chỉ định cho phép dừng. Sự kiện có thể là tốt đẹp cho các chủ đề đơn giản có một vòng lặp, nhưng các chủ đề phức tạp với xử lý phức tạp là một vấn đề khác. Chắc chắn, Tạm dừng() phải không bao giờ được sử dụng để đồng bộ hóa, vì tính hữu dụng của nó không dành cho chức năng này.

Chỉ quan điểm của tôi.

1

Tôi vừa triển khai lớp LoopingThread lặp lại một hành động được truyền cho hàm tạo. Nó dựa trên bài đăng của Brannon. Tôi đã đặt một số nội dung khác vào đó như WaitForPause(), WaitForStop() và thuộc tính TimeBetween, cho biết thời gian sẽ được chờ trước khi lặp tiếp theo.

Tôi cũng quyết định thay đổi vòng lặp while thành vòng lặp do-while. Điều này sẽ cho chúng ta một hành vi xác định cho một số liên tiếp Start()Pause(). Với định nghĩa, ý tôi là hành động được thực hiện ít nhất một lần sau lệnh Start(). Trong triển khai của Brannon, điều này có thể không đúng.

Tôi bỏ qua một số điều cho thư mục gốc của vấn đề.Những thứ như "kiểm tra xem chủ đề đã được bắt đầu chưa" hay mẫu IDisposable.

public class LoopingThread 
{ 
    private readonly Action _loopedAction; 
    private readonly AutoResetEvent _pauseEvent; 
    private readonly AutoResetEvent _resumeEvent; 
    private readonly AutoResetEvent _stopEvent; 
    private readonly AutoResetEvent _waitEvent; 

    private readonly Thread _thread; 

    public LoopingThread (Action loopedAction) 
    { 
    _loopedAction = loopedAction; 
    _thread = new Thread (Loop); 
    _pauseEvent = new AutoResetEvent (false); 
    _resumeEvent = new AutoResetEvent (false); 
    _stopEvent = new AutoResetEvent (false); 
    _waitEvent = new AutoResetEvent (false); 
    } 

    public void Start() 
    { 
    _thread.Start(); 
    } 

    public void Pause (int timeout = 0) 
    { 
    _pauseEvent.Set(); 
    _waitEvent.WaitOne (timeout); 
    } 

    public void Resume() 
    { 
    _resumeEvent.Set(); 
    } 

    public void Stop (int timeout = 0) 
    { 
    _stopEvent.Set(); 
    _resumeEvent.Set(); 
    _thread.Join (timeout); 
    } 

    public void WaitForPause() 
    { 
    Pause (Timeout.Infinite); 
    } 

    public void WaitForStop() 
    { 
    Stop (Timeout.Infinite); 
    } 

    public int PauseBetween { get; set; } 

    private void Loop() 
    { 
    do 
    { 
     _loopedAction(); 

     if (_pauseEvent.WaitOne (PauseBetween)) 
     { 
     _waitEvent.Set(); 
     _resumeEvent.WaitOne (Timeout.Infinite); 
     } 
    } while (!_stopEvent.WaitOne (0)); 
    } 
} 
0

Nếu không có yêu cầu đồng bộ hóa:

Thread.Sleep(Timeout.Infinite);

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