2010-09-13 61 views
7

Vấn đề tuyên bốLàm thế nào để chấm dứt một sợi công nhân một cách chính xác trong C#

Tôi có một sợi nhân viên rằng về cơ bản quét một thư mục, đi sâu vào các tập tin bên trong nó, và sau đó ngủ một thời gian. Quá trình quét có thể mất 2-3 giây nhưng không nhiều hơn nữa. Tôi đang tìm một cách để ngăn chặn chủ đề này một cách thanh lịch.

Làm rõ: Tôi muốn dừng thread khi nó ngủ, và không phải là lúc nó quét. Tuy nhiên, vấn đề là tôi không biết trạng thái hiện tại của luồng. Nếu nó đang ngủ tôi muốn nó thoát ngay lập tức. Nếu nó đang quét, tôi muốn nó thoát khỏi thời điểm nó cố chặn.

Nỗ lực tại một giải pháp

Lúc đầu, tôi đã sử dụng giấc ngủ và ngắt. Sau đó, tôi phát hiện ra rằng gián đoạn không thực sự làm gián đoạn giấc ngủ - nó chỉ hoạt động khi các chủ đề TRIES đi vào giấc ngủ.

Vì vậy, tôi đã chuyển sang Monitor Wait & Pulse. Sau đó, tôi phát hiện ra rằng Pulse chỉ hoạt động khi tôi thực sự ở trong Wait. Vì vậy, bây giờ tôi có một sợi trông giống như vậy:

while (m_shouldRun) 
{ 
    try 
    { 
     DoSomethingThatTakesSeveralSeconds(); 
     lock (this) 
     { 
      Monitor.Wait(this, 5000); 
     } 
    } 
    catch (ThreadInterruptedException) 
    { 
     m_shouldRun = false; 
    } 
} 

Và bây giờ tôi cần phải thủ công chức năng Dừng của tôi. Vì vậy, tôi bắt đầu với:

public void Stop() 
{ 
    m_shouldRun = false; 
    lock (this) 
    { 
     Monitor.Pulse(this); 
    } 
    thread.Join(); 
} 

Nhưng điều này không hoạt động vì tôi có thể đang rung khi chủ đề hoạt động (trong khi không chờ). Vì vậy, tôi đã thêm Interrupt:

public void Stop() 
{ 
    m_shouldRun = false; 
    thread.Interrupt(); 
    lock (this) 
    { 
     Monitor.Pulse(this); 
    } 
    thread.Join(); 
} 

Một lựa chọn khác là sử dụng:

public void Stop() 
{ 
    m_shouldRun = false; 
    while (!thread.Join(1000)) 
    { 
     lock (this) 
     { 
      Monitor.Pulse(this); 
     } 
    } 
} 

Câu hỏi

phương pháp ưa thích là gì? Có phương pháp thứ ba nào thích hợp hơn không?

+0

"* Lúc đầu, tôi đã sử dụng giấc ngủ và Interrupt Sau đó, tôi phát hiện ra rằng Ngắt không thực sự làm gián đoạn giấc ngủ - nó chỉ hoạt động khi các chủ đề TRIES đi ngủ. * "Thật kỳ lạ. Đây có phải là một lỗi được biết đến? Nếu nó không phải là, bạn có thể cố gắng để tái sản xuất nó với một ví dụ đồ chơi? – Heinzi

Trả lời

7

Một lựa chọn khác là sử dụng các sự kiện:

private ManualResetEvent _event = new ManualResetEvent(false); 


public void Run() 
{ 
while (true) 
{ 
    DoSomethingThatTakesSeveralSeconds(); 
    if (_event.WaitOne(timeout)) 
     break; 
} 
} 

public void Stop() 
{ 
    _event.Set(); 
    thread.Join(); 
} 
+0

Có, điều này cũng sẽ hoạt động. Câu hỏi đặt ra là cách tốt nhất để làm điều đó. Tùy chọn của bạn trông đẹp hơn Pulse định kỳ hoặc xung ngắt +. –

+0

+1, vâng, đây là * xa * tốt hơn. –

+1

Vâng, cá nhân tôi sẽ không sử dụng Pulse/Interrupts. Pulse có thể có vấn đề do các APC có thể (tôi tin rằng nó có thể không được chú ý bởi chủ đề). Gián đoạn không có danh tiếng tốt: http://www.bluebytesoftware.com/blog/2007/08/23/ThreadInterruptsAreAlmostAsEvilAsThreadAborts.aspx – liggett78

9

Cách để ngăn chặn chỉ một cách thanh lịch là để nó tự hoàn thành. Vì vậy, bên trong phương thức công nhân, bạn có thể có một biến boolean sẽ kiểm tra xem chúng ta có muốn ngắt hay không. Theo mặc định, nó sẽ được đặt thành false và khi bạn đặt nó thành true từ chủ đề chính, nó sẽ chỉ dừng hoạt động quét bằng cách ngắt khỏi vòng xử lý.

+3

+1 để cho phép chuỗi kết thúc bằng chính nó. Bất kỳ cách tiếp cận khác là lộn xộn. Đừng quên đánh dấu cờ boolean bằng từ khóa dễ bay hơi. – spender

+1

Cảm ơn. Bạn sẽ nhận thấy rằng tôi có một lá cờ như vậy. Tôi không làm gián đoạn sợi chỉ trong khi nó thực sự hoạt động, nhưng tôi muốn ngắt nó trong khi nó đang ngủ. Nếu nó ngủ 10 phút, tôi không muốn nó tiếp tục ngủ. –

+0

Chủ đề đang ngủ trong 10 giây không hữu ích cho bất kỳ ai. Sử dụng 'ThreadPool' để vẽ các chủ đề bất cứ khi nào bạn cần thực hiện một số tác vụ nhưng không để chúng ngủ. Làm cho họ làm những thứ hữu ích. –

1

Tôi khuyên bạn nên giữ nó đơn giản:

while (m_shouldRun) 
{ 
    DoSomethingThatTakesSeveralSeconds(); 
    for (int i = 0; i < 5; i++) // example: 5 seconds sleep 
    { 
     if (!m_shouldRun) 
      break; 
     Thread.Sleep(1000); 
    } 
} 

public void Stop() 
{ 
    m_shouldRun = false; 
    // maybe thread.Join(); 
} 

này có những ưu điểm sau:

  • Nó có mùi như bận rộn chờ đợi, nhưng nó không phải. $ NUMBER_OF_SECONDS kiểm tra được thực hiện trong giai đoạn chờ đợi, mà không thể so sánh với hàng ngàn kiểm tra được thực hiện trong chờ đợi thực sự bận rộn.
  • Thật đơn giản, giúp giảm thiểu nguy cơ lỗi trong mã đa luồng. Tất cả phương pháp Stop của bạn cần làm là đặt m_shouldRun thành sai và (có thể) gọi Thread.Join (nếu cần kết thúc chuỗi trước khi Stop còn lại).Không cần đồng bộ hóa nguyên gốc (ngoại trừ việc đánh dấu m_shouldRun là dễ bay hơi).
+0

Chức năng DoSomething sẽ không bị gián đoạn mạnh. Thread.Interrupt chỉ "xảy ra" khi luồng cố gắng chặn. Xem tài liệu MS (tại đây: http://msdn.microsoft.com/en-us/library/system.threading.thread.interrupt.aspx) - "Nếu chuỗi này hiện không bị chặn trong khi chờ, ngủ hoặc tham gia nhà nước, nó sẽ bị gián đoạn khi nó tiếp theo bắt đầu chặn. " –

+0

@Eldad: Tốt điểm, tôi nhầm lẫn nó với Thread.Abort. Thay đổi câu trả lời của tôi. – Heinzi

0

tôi đã đưa ra lịch riêng nhiệm vụ:.

using System; 
using System.Threading; 

namespace ProjectEuler 
{ 
    class Program 
    { 
     //const double cycleIntervalMilliseconds = 10 * 60 * 1000; 
     const double cycleIntervalMilliseconds = 5 * 1000; 
     static readonly System.Timers.Timer scanTimer = 
      new System.Timers.Timer(cycleIntervalMilliseconds); 
     static bool scanningEnabled = true; 
     static readonly ManualResetEvent scanFinished = 
      new ManualResetEvent(true); 

     static void Main(string[] args) 
     { 
      scanTimer.Elapsed += 
       new System.Timers.ElapsedEventHandler(scanTimer_Elapsed); 
      scanTimer.Enabled = true; 

      Console.ReadLine(); 
      scanningEnabled = false; 
      scanFinished.WaitOne(); 
     } 

     static void scanTimer_Elapsed(object sender, 
      System.Timers.ElapsedEventArgs e) 
     { 
      scanFinished.Reset(); 
      scanTimer.Enabled = false; 

      if (scanningEnabled) 
      { 
       try 
       { 
        Console.WriteLine("Processing"); 
        Thread.Sleep(5000); 
        Console.WriteLine("Finished"); 
       } 
       finally 
       { 
        scanTimer.Enabled = scanningEnabled; 
        scanFinished.Set(); 
       } 
      } 
     } 
    } 
} 
Các vấn đề liên quan