2017-11-29 4 views
-1

저는 Dataflow를 처음 사용하며이 연습을 통해 How to: Cancel a Dataflow Block을 따릅니다.
먼저 추가 단추를 클릭 한 다음 취소를 클릭하십시오. 취소 단추를 클릭하면 "작업이 취소되었습니다 예외" "에 대한 예외가 있습니다. 이 오류를 해결할 수있는 방법을 찾지 못했습니다.
도움이 될 것입니다.
업데이트 : 데모 코드 : @SirRufo는 지적 당신이 그것을 잡은 한 후Dataflow Task.WhenAll은 작업 취소를 발생시킵니다. 예외

public partial class Form1 : Form 
{ 
    CancellationTokenSource cancellationTokenSource; 
    TransformBlock<WorkItem, WorkItem> startWork; 
    ActionBlock<WorkItem> completeWork; 
    ActionBlock<ToolStripProgressBar> incProgress; 
    ActionBlock<ToolStripProgressBar> decProgress; 
    TaskScheduler uiTaskScheduler; 
    public Form1() 
    { 
     InitializeComponent(); 
     uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     Cancel.Enabled = false; 
    } 

    private void Add_Click(object sender, EventArgs e) 
    { 
     if (!Cancel.Enabled) 
     { 
      CreatePipeline(); 
      Cancel.Enabled = true; 
     } 

     for (int i = 0; i < 20; i++) 
     { 
      toolStripProgressBar1.Value++; 
      startWork.Post(new WorkItem()); 
     } 
    } 

    private async void Cancel_Click(object sender, EventArgs e) 
    { 
     Add.Enabled = false; 
     Cancel.Enabled = false; 

     cancellationTokenSource.Cancel(); 

     try 
     { 
      await Task.WhenAll(
       completeWork.Completion, 
       incProgress.Completion, 
       decProgress.Completion); 
     } 
     catch (OperationCanceledException) 
     { 

      throw; 
     } 
     toolStripProgressBar4.Value += toolStripProgressBar1.Value; 
     toolStripProgressBar4.Value += toolStripProgressBar2.Value; 

     // Reset the progress bars that track the number of active work items. 
     toolStripProgressBar1.Value = 0; 
     toolStripProgressBar2.Value = 0; 

     // Enable the Add Work Items button.  
     Add.Enabled = true; 
    } 
    private void CreatePipeline() 
    { 
     cancellationTokenSource = new CancellationTokenSource(); 

     startWork = new TransformBlock<WorkItem, WorkItem>(workItem => 
     { 
      workItem.DoWork(250, cancellationTokenSource.Token); 
      decProgress.Post(toolStripProgressBar1); 
      incProgress.Post(toolStripProgressBar2); 
      return workItem; 
     }, 
     new ExecutionDataflowBlockOptions 
     { 
      CancellationToken = cancellationTokenSource.Token 
     }); 

     completeWork = new ActionBlock<WorkItem>(workItem => 
     { 
      workItem.DoWork(1000, cancellationTokenSource.Token); 
      decProgress.Post(toolStripProgressBar2); 
      incProgress.Post(toolStripProgressBar3); 
     }, 
     new ExecutionDataflowBlockOptions 
     { 
      CancellationToken = cancellationTokenSource.Token, 
      MaxDegreeOfParallelism = 2 
     }); 

     startWork.LinkTo(completeWork); 

     startWork.Completion.ContinueWith(delegate { completeWork.Complete(); },cancellationTokenSource.Token); 
     incProgress = new ActionBlock<ToolStripProgressBar>(progress => 
     { 
      progress.Value++; 
     }, 
     new ExecutionDataflowBlockOptions 
     { 
      CancellationToken = cancellationTokenSource.Token, 
      TaskScheduler = uiTaskScheduler 
     }); 

     decProgress = new ActionBlock<ToolStripProgressBar>(progress => progress.Value--, 
      new ExecutionDataflowBlockOptions 
      { 
       CancellationToken = cancellationTokenSource.Token, 
       TaskScheduler = uiTaskScheduler 
      }); 

    } 

    class WorkItem 
    { 
     public void DoWork(int milliseconds, CancellationToken cancellationToken) 
     { 
      if (cancellationToken.IsCancellationRequested == false) 
      { 
       Thread.Sleep(milliseconds); 
      } 
     } 
    } 
} 
+0

큰 튜토리얼에 대한 링크를 게시하는 대신 관련 코드를 직접 게시하는 것이 좋습니다. – Evk

+0

그럼 작업을 취소하면 TaskCanceledException을 얻는 것에 놀랄 필요가 없습니다. 그냥 당신이 한 것을 반영합니다 –

+0

@SirRufo이 예외를 처리하는 대신이 예외를 피하는 방법? –

답변

2

, 질문에 대한 솔루션은 단순히 예외를 다시 포기하지 않습니다. 그러나 주석에 설명 된대로 데이터 흐름과 함께 사용할 수있는 몇 가지 다른 기술을 강조하기 위해 작은 샘플을 정리했습니다. 나는 원본 코드의 의도와 정신을 손상시키지 않으려 고 노력했다. 마지막까지; 원래 코드는 흐름이 정상적으로 완료되는 방법을 보여주지 않았으며, 취소 된 것과는 반대로 여기에서도 빠져 나갔습니다.

using System; 
using System.Data; 
using System.Linq; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Threading.Tasks.Dataflow; 
using System.Windows.Forms; 

namespace WindowsFormsApp1 { 
    public partial class Form1 : Form { 

     private CancellationTokenSource cancellationTokenSource; 
     private TransformBlock<WorkItem, WorkItem> startWork; 
     private ActionBlock<WorkItem> completeWork; 
     private IProgress<int> progressBar1Value; 
     private IProgress<int> progressBar2Value; 

     public Form1() { 
      InitializeComponent(); 
      btnCancel.Enabled = false; 
     } 

     private async void btnAdd_Click(object sender, EventArgs e) { 
      if(!btnCancel.Enabled) { 
       CreatePipeline(); 
       btnCancel.Enabled = true; 
      } 
      var data = Enumerable.Range(0, 20).Select(_ => new WorkItem()); 
      foreach(var workItem in data) { 
       await startWork.SendAsync(workItem); 
       progressBar1.Value++;     
      } 
     } 

     private async void btnCancel_Click(object sender, EventArgs e) { 
      btnAdd.Enabled = false; 
      btnCancel.Enabled = false; 

      cancellationTokenSource.Cancel(); 

      await completeWork.Completion.ContinueWith(tsk => this.Invoke(new Action(() => this.Text = "Flow Cancelled")), 
                 TaskContinuationOptions.OnlyOnCanceled); 

      progressBar4.Value += progressBar1.Value; 
      progressBar4.Value += progressBar2.Value; 

      // Reset the progress bars that track the number of active work items. 
      progressBar1.Value = 0; 
      progressBar2.Value = 0; 

      // Enable the Add Work Items button.  
      btnAdd.Enabled = true; 
     } 

     private void CreatePipeline() { 
      cancellationTokenSource = new CancellationTokenSource(); 
      progressBar1Value = new Progress<int>(_ => progressBar1.Value++); 
      progressBar2Value = new Progress<int>(_ => progressBar2.Value++); 

      startWork = new TransformBlock<WorkItem, WorkItem>(async workItem => { 
       await workItem.DoWork(250, cancellationTokenSource.Token); 
       progressBar1Value.Report(0); //Value is ignored since the progressbar value is simply incremented 
       progressBar2Value.Report(0); //Value is ignored since the progressbar value is simply incremented 
       return workItem; 
      }, 
      new ExecutionDataflowBlockOptions { 
       CancellationToken = cancellationTokenSource.Token 
      }); 

      completeWork = new ActionBlock<WorkItem>(async workItem => { 
       await workItem.DoWork(1000, cancellationTokenSource.Token); 
       progressBar1Value.Report(0); //Value is ignored since the progressbar value is simply incremented 
       progressBar2Value.Report(0); //Value is ignored since the progressbar value is simply incremented 
      }, 
      new ExecutionDataflowBlockOptions { 
       CancellationToken = cancellationTokenSource.Token, 
       MaxDegreeOfParallelism = 2 
      }); 

      startWork.LinkTo(completeWork, new DataflowLinkOptions() { PropagateCompletion = true }); 
     } 
    } 

    public class WorkItem { 
     public async Task DoWork(int milliseconds, CancellationToken cancellationToken) { 
      if(cancellationToken.IsCancellationRequested == false) { 
       await Task.Delay(milliseconds); 
      } 
     } 
    } 
} 
0

코드를 확인한 후 취소를 클릭하면 작업이 취소된다고 발표했습니다. 모든 작업이 필요 Task.WhenAll 코드 위에

await Task.WhenAll(
       completeWork.Completion, 
       incProgress.Completion, 
       decProgress.Completion); 

하지만, 완전한 상태를 반환 한 후 "태스크가 취소되었습니다 예외"이 취소 대신 완료 반환하는 경우 예상대로 던져.
이 문제를 해결할 수있는 방법은 작업을 취소하고 작업 코드가 아래에 나와있는 경우 작업 완료를 반환해야합니다.

 await Task.WhenAll(
      completeWork.Completion.ContinueWith(task => cancelWork(task, "completeWork"), TaskContinuationOptions.OnlyOnCanceled), 
      incProgress.Completion.ContinueWith(task => cancelWork(task, "incProgress"), TaskContinuationOptions.OnlyOnCanceled), 
      decProgress.Completion.ContinueWith(task => cancelWork(task, "decProgress"), TaskContinuationOptions.OnlyOnCanceled)); 

합리적인가요?

관련 문제