2009-07-17 24 views
5

Tôi đang có một chuỗi nền nhỏ chạy suốt đời ứng dụng - tuy nhiên khi ứng dụng bị tắt, luồng sẽ thoát ra một cách duyên dáng.Thread.Interrupt để ngừng ngủ dài khi tắt ứng dụng - Có cách tiếp cận tốt hơn

Vấn đề là chuỗi chạy một số mã trong khoảng thời gian 15 phút - có nghĩa là nó ngủ ALOT.

Bây giờ để thoát khỏi giấc ngủ, tôi quăng một gián đoạn vào nó - câu hỏi của tôi là tuy nhiên, nếu có cách tiếp cận tốt hơn cho điều này, vì các ngắt tạo ra ThreadInterruptedException.

Dưới đây là các ý chính của mã của tôi (hơi giả):

public class BackgroundUpdater : IDisposable 
{ 
    private Thread myThread; 
    private const int intervalTime = 900000; // 15 minutes 
    public void Dispose() 
    { 
     myThread.Interrupt(); 
    } 

    public void Start() 
    { 
     myThread = new Thread(ThreadedWork); 
     myThread.IsBackground = true; // To ensure against app waiting for thread to exit 
     myThread.Priority = ThreadPriority.BelowNormal; 
     myThread.Start(); 
    } 

    private void ThreadedWork() 
    { 
     try 
     { 
      while (true) 
      { 
       Thread.Sleep(900000); // 15 minutes 
       DoWork(); 
      } 
     } 
     catch (ThreadInterruptedException) 
     { 
     } 
    } 
} 

Trả lời

14

Có hoàn toàn một cách tốt hơn - hoặc sử dụng Monitor.Wait/Pulse thay vì Sleep/ngắt, hoặc sử dụng một Auto/ManualResetEvent. (Có thể bạn muốn có một ManualResetEvent trong trường hợp này.)

Cá nhân tôi là một người hâm mộ Wait/Pulse, có lẽ do nó giống như cơ chế wait()/notify() của Java. Tuy nhiên, có những thời điểm chắc chắn khi sự kiện đặt lại hữu ích hơn.

Mã của bạn sẽ giống như thế này:

private readonly object padlock = new object(); 
private volatile bool stopping = false; 

public void Stop() // Could make this Dispose if you want 
{ 
    stopping = true; 
    lock (padlock) 
    { 
     Monitor.Pulse(padlock); 
    } 
} 

private void ThreadedWork() 
{ 
    while (!stopping) 
    { 
     DoWork(); 
     lock (padlock) 
     { 
      Monitor.Wait(padlock, TimeSpan.FromMinutes(15)); 
     } 
    } 
} 

Để biết thêm chi tiết, xem threading tutorial của tôi, đặc biệt là các trang trên deadlocks, waiting and pulsing, trang trên wait handles. Joe Albahari also has a tutorial bao gồm các chủ đề tương tự và so sánh chúng.

Tôi chưa xem chi tiết, nhưng tôi sẽ không ngạc nhiên nếu tiện ích mở rộng song song cũng có một số chức năng để thực hiện việc này dễ dàng hơn.

+0

Bạn quá nhanh. Tôi bỏ cuộc;) – frast

+0

Giải pháp tuyệt vời, cảm ơn rất nhiều :-) – Steffen

+2

Không phải là vấn đề ở đây là chuỗi làm việc đang đợi trong khi giữ khóa, để khóa trong phương pháp stop() sẽ chặn trong tối đa 15 phút cho đến khi làm việc thread phát hành khóa? –

0

Một phương pháp có thể là thêm sự kiện hủy hoặc ủy quyền mà chuỗi sẽ đăng ký. Khi sự kiện hủy được gọi, luồng có thể tự dừng lại.

2

Bạn có thể sử dụng một sự kiện để Kiểm tra xem trình nên kết thúc như thế này:

var eventX = new AutoResetEvent(false); 
while (true) 
{ 
    if(eventX.WaitOne(900000, false)) 
    { 
     break; 
    } 
    DoWork(); 
} 
0

Tôi hoàn toàn như Jon Skeets câu trả lời. Tuy nhiên, sức này là một chút dễ dàng hơn để hiểu và cũng nên làm việc:

public class BackgroundTask : IDisposable 
{ 
    private readonly CancellationTokenSource cancellationTokenSource; 
    private bool stop; 

    public BackgroundTask() 
    { 
     this.cancellationTokenSource = new CancellationTokenSource(); 
     this.stop = false; 
    } 

    public void Stop() 
    { 
     this.stop = true; 
     this.cancellationTokenSource.Cancel(); 
    } 

    public void Dispose() 
    { 
     this.cancellationTokenSource.Dispose(); 
    } 

    private void ThreadedWork(object state) 
    { 
     using (var syncHandle = new ManualResetEventSlim()) 
     { 
      while (!this.stop) 
      { 
       syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token); 
       if (!this.cancellationTokenSource.IsCancellationRequested) 
       { 
        // DoWork(); 
       } 
      } 
     } 
    } 
} 

Hoặc, kể cả chờ đợi cho các nhiệm vụ nền để thực sự đã ngừng (trong trường hợp này, Vứt bỏ phải được gọi bởi thread khác với một thread nền đang chạy trên, và tất nhiên đây không phải là mã hoàn hảo, nó đòi hỏi các sợi nhân để thực sự đã bắt đầu):

using System; 
using System.Threading; 

public class BackgroundTask : IDisposable 
{ 
    private readonly ManualResetEventSlim threadedWorkEndSyncHandle; 
    private readonly CancellationTokenSource cancellationTokenSource; 
    private bool stop; 

    public BackgroundTask() 
    { 
     this.threadedWorkEndSyncHandle = new ManualResetEventSlim(); 
     this.cancellationTokenSource = new CancellationTokenSource(); 
     this.stop = false; 
    } 

    public void Dispose() 
    { 
     this.stop = true; 
     this.cancellationTokenSource.Cancel(); 
     this.threadedWorkEndSyncHandle.Wait(); 
     this.cancellationTokenSource.Dispose(); 
     this.threadedWorkEndSyncHandle.Dispose(); 
    } 

    private void ThreadedWork(object state) 
    { 
     try 
     { 
      using (var syncHandle = new ManualResetEventSlim()) 
      { 
       while (!this.stop) 
       { 
        syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token); 
        if (!this.cancellationTokenSource.IsCancellationRequested) 
        { 
         // DoWork(); 
        } 
       } 
      } 
     } 
     finally 
     { 
      this.threadedWorkEndSyncHandle.Set(); 
     } 
    } 
} 

Nếu bạn thấy bất kỳ sai sót và nhược điểm so với giải pháp Jon Skeets tôi muốn để nghe chúng như tôi luôn luôn thích học tập ;-) Tôi đoán đây là s thấp hơn và sử dụng nhiều bộ nhớ hơn và do đó sẽ không được sử dụng ở quy mô lớn và khung thời gian ngắn. Còn ai khác không?

1

CancellationTokenSource lớp trong .NET 4 và sau đó giúp đơn giản hóa tác vụ này một chút.

private readonly CancellationTokenSource cancellationTokenSource = 
    new CancellationTokenSource(); 

private void Run() 
{ 
    while (!cancellationTokenSource.IsCancellationRequested) 
    { 
     DoWork(); 
     cancellationTokenSource.Token.WaitHandle.WaitOne(
      TimeSpan.FromMinutes(15)); 
    } 
} 

public void Stop() 
{ 
    cancellationTokenSource.Cancel(); 
} 

Đừng quên rằng CancellationTokenSource là dùng một lần, vì vậy hãy đảm bảo bạn vứt bỏ đúng cách.

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