【问题标题】:Use a CancellationToken to interrupt nested tasks使用 CancellationToken 中断嵌套任务
【发布时间】:2017-09-15 22:45:16
【问题描述】:

这是我的场景:用户单击一个 WPF 按钮,该按钮启动一个开放式时间段,用于在地图上收集点。当用户点击“收集完毕”按钮时,我希望CollectPoints()任务完成。

这是我的 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) 行上设置断点并单击取消按钮时,我永远不会到达那个点。

我在这里以正确的方式使用取消令牌吗?

【问题讨论】:

  • 附注:您不应该将async void 用于不是事件处理程序的方法。
  • 您还需要在CollectPoint 中处理取消,这可能就是它现在正在等待的地方。
  • 一旦用户停止收集积分,他们需要至少再收集一个才能完成CollectPoint 任务,这将暂停执行您的while。在CollectPoint 外面按住tcs 并在用户想要停止收集时完成或取消它,然后您的while 将按预期完成。

标签: c# wpf async-await


【解决方案1】:

在您调用CancellationTokenSourceCancel() 方法后,只要它第一次满足while 条件!token.IsCancellationRequestedCollectPoints 方法就会返回。

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

【讨论】:

  • 我的解决方案与此非常相似,只是我还创建了一个新的Task 并将我的令牌传递给CollectPoint,如下所示:var point = await Task.Run(CollectPoint(token), token);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 2019-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多