2017-09-15 36 views
3

以下是我的場景:用戶單擊一個WPF按鈕,該按鈕啓動地圖上點集合的開放式時段。當用戶點擊「完成收集」按鈕時,我想完成CollectPoints()任務。使用CancellationToken中斷嵌套任務

這裏是我的SegmentRecorder類的作品:

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

這裏是我的視圖模型的相關部分:

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

當我把一個斷點就行了while (!token.IsCancellationRequested),點擊取消按鈕,我從來沒有達到這一點。

我是否在這裏以正確的方式使用取消標記?

+2

旁註:你不應該使用'異步void'因爲這是不是一個事件處理程序的方法。 – dymanoid

+0

您還需要處理'CollectPoint'內的取消,這可能就是它現在正在等待的地方。 – JSteward

+2

一旦用戶停止收集點數,他們需要至少收集一個'CollectPoint'任務來完成暫停執行'while'的任務。在'CollectPoint'之外放置'tcs',並在用戶想要停止收集時完成或取消它,然後您的時間將按預期完成。 – JSteward

回答

4

CollectPoints方法將返回只要它擊中while條件!token.IsCancellationRequested在你第一次叫CancellationTokenSourceCancel()方法之後。

該任務不會被取消,而代碼內部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 = await Task.Run(CollectPoint(token),token) – wdonahoe

相關問題