2017-09-15 5 views
3

ここに私のシナリオがあります:ユーザーが地図上のポイントの集まりを無期限にするWPFボタンをクリックします。ユーザーが「収集完了」ボタンをクリックすると、CollectPoints()タスクを完了します。ここでCancellationTokenを使用してネストされたタスクを中断する

は私SegmentRecorderクラスの一部です:私はラインwhile (!token.IsCancellationRequested)にブレークポイントを入れて、キャンセルボタンをクリックすると

public SegmentRecorder SegmentRecorder { get; } 
public RelayCommand StopSegment { get; } 

public ViewModel(){ 
    StopSegment = new RelayCommand(ExecuteStopSegment); 
    SegmentRecorder = new SegmentRecorder(); 
} 

// execute on cancel button click. 
public void ExecuteStopSegment(){ 
    SegmentRecorder.StopSegment(); 
} 

:ここ

private CancellationTokenSource _cancellationToken;  

    public virtual async void RecordSegment(IRoadSegment segment) 
    { 
     _cancellationToken = new CancellationTokenSource(); 
     var token = _cancellationToken.Token; 

     // await user clicking points on map 
     await CollectPoints(token); 

     // update the current segment with the collected shape. 
     CurrentSegment.Shape = CurrentGeometry as Polyline; 
    } 

    // collect an arbitrary number of points and build a polyline. 
    private async Task CollectPoints(CancellationToken token) 
    { 
     var points = new List<MapPoint>(); 
     while (!token.IsCancellationRequested) 
     { 
      // wait for a new point. 
      var point = await CollectPoint(); 
      points.Add(point); 

      // add point to current polyline 
      var polylineBuilder = new PolylineBuilder(points, SpatialReferences.Wgs84); 
      CurrentGeometry = polylineBuilder.ToGeometry(); 

      // draw points 
      MapService.DrawPoints(CurrentGeometry); 
     } 
    } 

    // collect a point from map click. 
    protected override Task<MapPoint> CollectPoint() 
    { 
     var tcs = new TaskCompletionSource<MapPoint>(); 
     EventHandler<IMapClickedEventArgs> handler = null; 
     handler = (s, e) => 
     { 
      var mapPoint = e.Geometry as MapPoint; 
      if (mapPoint != null) 
      { 
       tcs.SetResult(new MapPoint(mapPoint.X, mapPoint.Y, SpatialReferences.Wgs84)); 
      } 
      MapService.OnMapClicked -= handler; 
     }; 
     MapService.OnMapClicked += handler; 

     return tcs.Task; 
    } 

    public void StopSegment(){ 
     // interrupt the CollectPoints task. 
     _cancellationToken.Cancel(); 
    } 

は、私の見解モデルの関連部分です、私はその点に決して到達しません。

キャンセルトークンを正しい方法で使用していますか?それはwhile条件!token.IsCancellationRequestedあなたがCancellationTokenSourceCancel()メソッドを呼び出した後、初めてに当たるたび

+2

サイドノート:あなたは、イベントハンドラではありません方法のために '非同期void'を使用しないでください。 – dymanoid

+0

あなたは 'CollectPoint'の中で取り消しを処理する必要があります、それはおそらく今どこに待っているのでしょうか。 – JSteward

+2

ユーザーがポイントの収集を停止すると、少なくとも 'CollectPoint'タスクが完了するために少なくとももう1つは収集する必要があり、これはあなたの' while'の実行を一時停止しています。 'CollectPoint'の外側で' tcs'をホールドして、それを完了させるか、またはユーザーが収集を停止したいときにキャンセルします。すると、あなたの期待どおりに終了します。 – JSteward

答えて

4

CollectPoints方法が返されます。

whileループ内でコードが実行されている間、タスクはキャンセルされません。

@JSteward氏のコメントでは、StopSegment()メソッドのTaskCompletionSourceをキャンセルまたは完了する必要があります。このような

何か:

public virtual async void RecordSegment(IRoadSegment segment) 
{ 
    _cancellationToken = new CancellationTokenSource(); 
    var token = _cancellationToken.Token; 

    // await user clicking points on map 
    await CollectPoints(token); 

    // update the current segment with the collected shape. 
    CurrentSegment.Shape = CurrentGeometry as Polyline; 
} 

// collect an arbitrary number of points and build a polyline. 
private async Task CollectPoints(CancellationToken token) 
{ 
    var points = new List<MapPoint>(); 
    while (!token.IsCancellationRequested) 
    { 
     try 
     { 
      // wait for a new point. 
      var point = await CollectPoint(token); 

      //... 
     } 
     catch (Exception) { } 
    } 
} 

private TaskCompletionSource<MapPoint> tcs; 
protected override Task<MapPoint> CollectPoint() 
{ 
    tcs = new TaskCompletionSource<MapPoint>(); 
    //... 
    return tcs.Task; 
} 

public void StopSegment() 
{ 
    // interrupt the CollectPoints task. 
    _cancellationToken.Cancel(); 
    tcs.SetCanceled(); 
} 
+0

私の解決策はこれと非常に似ていました。私は新しい 'Task'を作成し、自分のトークンを' CollectPoint'に渡しました。 'var point = Task.Run(CollectPoint(token)、token);' – wdonahoe

関連する問題