2012-05-02 3 views
11

제 질문은 어떻게 장기 실행 작업 (.net 4)을 중지 할 수 있습니까? TPL을 구현하고 CancellationTokenSource를 사용해 보았지만 제 시나리오에서는 작동하지 않습니다. 필자가 보았던 모든 예제는 while 루프에서 작업을 수행 중이므로 작업이 취소되었는지 확인할 수 있도록하는 반면, 필자는 오래 걸리는 단일 작업 만 수행한다고 가정합니다. 나는 그것이 완료되지 않을 것이라고 가정 할 필요가 있기 때문에 작업이 완료 될 때까지 기다릴 수 없습니다.장기 실행 작업을 안전하게 중단하십시오.

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

코드는 작동하지만 "타임 아웃"이 발생하고 수행되고있는 작업 즉 자원 "관리되지 않는 코드"를 액세스하는 경우 작업이 배치되지 않습니다 : 는 여기에 내가 시도 코드입니다. IsCancelledRequested는 Work.LongRunning()에서 사용할 수 없으므로 ThrowIfCancellationRequested를 사용할 수 없습니다.

나는 BackgroundWorker를 시도했지만 다른 아이디어에 대해서도 개방적이지만 그 또한 적합하지 않은 것 같습니다.

새로운 예 :

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

답변

2

작업 병렬 라이브러리는 CPU 집약적 인 작업을 위해 설계되었습니다. CPU 집약적 인 작업은 잠시 이루어집니다. Work.LongRunning()이 CPU를 많이 사용하는 경우 취소 토큰을 내부에 전달하고 취소 할 수 있어야합니다. CPU 집약적이지 않은 경우 최종 콜백에서 결과를 그냥 버리고 그냥 기다리고 있기 때문에 실제 작업을 중단하지 않아도됩니다.

BTW 대기중인 경우 (데이터베이스 호출 등) 어쩌면 맨 아래 어딘가에 비동기 메소드가있을 수 있습니다. Begin/End 패턴을 표면화하고이를 Task에 포장 할 수 있습니다. 이 질문은 방법을 설명합니다 : TPL TaskFactory.FromAsync vs Tasks with blocking methods 이렇게하면 IO 대기가 OS에서 처리하는 특별한 방법으로 수행되기 때문에 범용 스레드를 사용하지 않아도됩니다.

+0

감사합니다! Begin \ End 패턴을 살펴 봤지만 End의 결과를 확인하려면 (() isyncResult) 작업이 완료 될 때까지 기다려야합니다. 나는 나의 예를 들어 업데이트했다. 값을 확인하자마자 ServiceController의 프로퍼티가 게으른로드 된 것처럼 보입니다. – nickv

+0

코드를 완벽하게 따라갈 수는 없지만 ContinueWith를 사용하여 연속을 추가하지 않는 이유는 무엇입니까? ? – Stilgar

+0

새로운 일을 계속하고 싶지 않기 때문에. 내가 수행하고자하는 하나의 작업이 있는데, 그 작업은 어떤 이유로 인해 응답을받지 못할 수 있습니다. 따라서 Canceled가 호출 되었더라도 작업이 실행 상태에있을 것입니다. – nickv

2

찾는 옵션 1 obove 설명하는 예 (즉, 단지 소거 신호없이 태스크를 죽이는)의 응답

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

사실,이 코드는 장기간 실행되는 doSomething 작업을 중단시키지 않습니다. "c"키를 누르면 "작업이 취소되었습니다"라는 메시지가 표시되지만 doSomething 작업은 계속 실행됩니다. 30 초 정도 기다리면 콘솔에서 "지금 깨어나세요"라는 텍스트가 나옵니다. –