2012-05-02 11 views
11

Câu hỏi của tôi là làm thế nào tôi có thể dừng một tác vụ chạy dài (.net 4)? Tôi đã triển khai TPL và đã thử sử dụng CancellationTokenSource nhưng dường như nó không hoạt động cho kịch bản của tôi. Tất cả các ví dụ tôi đã thấy giả sử bạn đang làm việc trong một vòng lặp while để bạn có thể kiểm tra xem tác vụ đã bị hủy hay chưa, trong khi tôi chỉ có một hoạt động duy nhất mất nhiều thời gian. Tôi không thể chờ đợi cho công việc được hoàn thành như tôi cần phải giả định nó có thể không bao giờ hoàn thành. Đây là mã tôi đã cố gắng:Tạm dừng nhiệm vụ chạy dài một cách an toàn

 bool? result = null; 

     var cs = new CancellationTokenSource(); 
     var ct = cs.Token; 

     var doWorkTask = new Task(() => 
     { 
      Console.WriteLine("start dowork task"); 

      result = Work.LongRunning(); 
     }, ct); 

     doWorkTask.Start(); 

     Task.WaitAny(new Task[] { doWorkTask }, timetowait); 

     if (doWorkTask.IsCompleted) 
     { 
     Console.WriteLine("dowork task completed"); 

      doWorkTask.Dispose(); 
     } 
     else 
     { 
     Console.WriteLine("dowork task has timedout"); 

      cs.Cancel(); 

      throw new TimeoutException("Timeout hit."); 
     } 

Mã này hoạt động nhưng nhiệm vụ là không bao giờ vứt bỏ nếu “timeout” xảy ra và công việc đang được thực hiện truy cập “mã không được quản lý”, tức là nguồn lực. Điều đó nói rằng IsCancelledRequested không thể được sử dụng trong Work.LongRunning() vì vậy tôi không thể ThrowIfCancellationRequested.

Tôi mở cho các ý tưởng khác cũng như tôi đã thử BackgroundWorker nhưng điều đó cũng có vẻ không phù hợp.

dụ mới:

var service = new System.ServiceProcess.ServiceController(ServiceName, ServerName); 

     var serviceTask = Task.Factory.StartNew(() => 
     { 
      result = (service.Status == ServiceControllerStatus.Running 
       || service.Status == ServiceControllerStatus.StartPending); 
     }, cs.Token); 

     serviceTask.Wait(2000, cs.Token); 

     if (!serviceTask.IsCompleted) 
     { 
      cs.Cancel(); 
     } 

Trả lời

2

công tác song song Thư viện được thiết kế cho CPU làm việc chuyên sâu. Công việc chuyên sâu của CPU được thực hiện trong một thời gian. Nếu Work.LongRunning() của bạn là CPU chuyên sâu, bạn sẽ có thể vượt qua mã thông báo hủy bên trong và hủy bỏ nó. Nếu nó không phải là CPU chuyên sâu thì bạn chỉ có thể loại bỏ kết quả trong một cuộc gọi lại cuối cùng và không bận tâm với việc dừng công việc thực tế vì nó chỉ là chờ đợi.

BTW nếu bạn có chờ đợi (đối với cuộc gọi cơ sở dữ liệu hoặc một cái gì đó) bạn có thể có phương pháp không đồng bộ ở đâu đó ở phía dưới. Bạn có thể hiển thị mẫu Bắt đầu/Kết thúc và bọc nó trong một Tác vụ. Câu hỏi này giải thích làm thế nào: TPL TaskFactory.FromAsync vs Tasks with blocking methods Bằng cách này bạn sẽ tránh hogging một chủ đề mục đích chung kể từ khi IO chờ đợi được thực hiện một cách đặc biệt xử lý bởi hệ điều hành.

+0

Cảm ơn câu trả lời! Tôi đã xem mẫu Begin \ End, nhưng sau đó kiểm tra kết quả trên End's ((Task ) isyncResult) .Result bạn phải đợi cho đến khi nó hoàn thành. Tôi đã cập nhật ví dụ của mình. Khi các thuộc tính trên ServiceController dường như được tải chậm ngay khi bạn kiểm tra một giá trị có thể mất bao lâu ... – nickv

+0

Tôi phải thừa nhận rằng tôi không thể làm theo mã của bạn hoàn toàn nhưng tại sao bạn không thêm tiếp tục với ContinueWith ? – Stilgar

+0

Bởi vì tôi không muốn tiếp tục với một nhiệm vụ mới. Tôi có một nhiệm vụ mà tôi muốn thực hiện, nhiệm vụ đó có thể không mang lại một phản ứng do bất cứ điều gì vì vậy nhiệm vụ sẽ ở trạng thái chạy mãi mãi mặc dù bị hủy bỏ được gọi. – nickv

2

Dưới đây là một ví dụ cho phương án 1 được mô tả obove (nghĩa là chỉ giết chết các công tác mà không cần tín hiệu hủy)

class Program 
    { 
     private static void Main(string[] args) 
     { 
      Test test = new Test(); 
      test.Run(); 

      Console.WriteLine("Type c to cancel"); 
      if (Console.ReadLine().StartsWith("c")) 
      { 
       Console.WriteLine("cancellation requested"); 
       test.CancellationTokenSource.Cancel(); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public class Test 
    { 
     private void DoSomething() 
     { 
      Console.WriteLine("DoSomething runs for 30 seconds "); 
      Thread.Sleep(new TimeSpan(0, 0, 0, 30)); 
      Console.WriteLine("woke up now "); 
     } 

     public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); 

     public void Run() 
     { 
       var generateReportsTask = Task.Factory.StartNew(() => 
       { 
        CancellationTokenSource.Token.ThrowIfCancellationRequested(); 
        Task doSomething = new Task(DoSomething, CancellationTokenSource.Token); 
        doSomething.Start(); 

        doSomething.Wait(CancellationTokenSource.Token); 
       }, CancellationTokenSource.Token); 

       generateReportsTask.ContinueWith(
        (t) => 
        { 
         if (t.Exception != null) 
          Console.WriteLine("Exceptions reported :\n " + t.Exception); 

         if (t.Status == TaskStatus.RanToCompletion) 
          Console.WriteLine("Completed report generation task"); 
         if (t.Status == TaskStatus.Faulted) 
          Console.WriteLine("Completed reported generation with unhandeled exceptions"); 
         if(t.Status == TaskStatus.Canceled) 
          Console.WriteLine("The Task Has been cancelled"); 
        }); 

     } 
    } 
+3

Trong thực tế, mã này không giết nhiệm vụ doSomething chạy dài. Khi bạn nhấn "c", bạn sẽ nhận được "Tác vụ Đã bị hủy" nhưng tác vụ doSomething vẫn chạy. Chỉ cần đợi 30 giây và bạn sẽ nhận được văn bản "thức dậy ngay bây giờ" trong bảng điều khiển. –