【问题标题】:How to cancel Task C#如何取消任务 C#
【发布时间】:2018-09-05 01:43:52
【问题描述】:

我有一个从 Web 服务检索一些数据的任务。 Web 服务应该只在我打开一个页面(这是 Xamarin.Forms)时被调用,并且它应该只在没有另一个版本的任务运行时才运行 - 这部分工作正常。

当我离开页面时,我想取消任务 - 我似乎无法让那部分工作。当OnAppearing()OnDisappearing()OnAppearing() is hit before the webservice returns the data, logs just write "Task has attempted to start..." which means that the Task didn't get cancelled. Instead the task should have been cancelled whenOnDisappearing()`被调用时。

我尝试按照 StackOverflow 上发布的一些示例进行操作,但似乎没有用,我也不太明白。

我的代码如下:

出现时:

private Task task;
CancellationTokenSource tokenSource = new CancellationTokenSource();

protected override void OnAppearing()
{
    base.OnAppearing();

    if (task != null && (task.Status == TaskStatus.Running || task.Status == TaskStatus.WaitingToRun || task.Status == TaskStatus.WaitingForActivation))
    {
        Debug.WriteLine("Task has attempted to start while already running");
    }
    else
    {
        Debug.WriteLine("Task has began");

        var token = tokenSource.Token;
        task = Task.Run(async () =>
        {

            await GetDataAsync();
            /* EDIT: This is what I originally posted, but doesn't work so have commented it out
            while (!token.IsCancellationRequested)
            {
                await GetDataAsync();
            }
             */
       }, token);
   }      
}

OnDisappearing:

protected override void OnDisappearing()
{
    base.OnDisappearing();
    Debug.WriteLine("Page dissapear");
    tokenSource.Cancel();
    Debug.WriteLine("Task Cancelled");
}

获取数据方法

public async Task GetData()
{
    WebAPI api = new WebAPI();

    try
    {
        Device.BeginInvokeOnMainThread(() =>
        {
            PageLoading();
        });

        string r = await api.GetProfileStatus(token);
        Device.BeginInvokeOnMainThread(async () =>
        {
           if (r == "OK")
           {
                GetDataSuccess();
           }
        }
     }
     catch (Exception e)
     {
        // log exception
     }
}

【问题讨论】:

  • GetData 从哪里获取令牌?和上面一样吗?
  • @Steve 下次适当地间隔你的代码,它使它更具可读性
  • @SamiKuhmonen 我不确定。我以为“Task.Run(async () =>...”中传入的token就是token?这个真的不是很熟悉。
  • @johnny5 好的,请注意-谢谢。
  • 删除 Task.Run - 这是不必要的,因为 GetData(Async) 已经是异步的,因此无需将其包装在另一个任务中 - 并为 GetData(Async) 提供取消令牌。取消的方式和是否有效取决于 GetProfileStatus 的实施方式。

标签: c# xamarin.forms async-await task-parallel-library cancellation


【解决方案1】:

你在错误的地方做正确的检查:

var token = tokenSource.Token;
task = Task.Run(async () =>
{
    while (!token.IsCancellationRequested)
    {
        await GetDataAsync();
    }
}

在这里您开始任务,然后在循环中一次又一次地启动它。正确的做法是检查GetData 中的令牌,如果可能,再检查api 变量。在这种情况下,您可以根据需要取消服务调用。

旁注:

  • 使您的事件处理程序async,并且不要将您的工作包装在Task.Run
  • 让你的GetDataTask<T>,这样你就可以真正将状态返回给调用代码,然后你可以删除BeginInvokeOnMainThread,因为await将在异步操作后恢复上下文

所以你的代码会是这样的:

protected override async void OnAppearing()
{
    base.OnAppearing();

    if (task != null && (task.Status == TaskStatus.Running || task.Status == TaskStatus.WaitingToRun || task.Status == TaskStatus.WaitingForActivation))
    {
        Debug.WriteLine("Task has attempted to start while already running");
    }
    else
    {
        Debug.WriteLine("Task has began");

        var token = tokenSource.Token;
        PageLoading();
        var r = await GetDataAsync(token);
        if (r == "OK")
        {
            GetDataSuccess();
        }
   }      
}

public async Task GetDataAsync(CancellationToken token)
{
    WebAPI api = new WebAPI();
    if (token.IsCancellationRequested)
    {
        token.ThrowIfCancellationRequested();
    }

    try
    {
        return await api.GetProfileStatus(token);
    }
    catch (Exception e)
    {
        // log exception and return error
        return "Error";
    }
}

【讨论】:

  • 您好,谢谢!我会试一试,看看效果如何。我不确定 OnAppearing 是否可以标记为异步,因为它是从 Xamarin.Forms 继承的,所以这就是我使用 Task.Run 的原因。
  • 看起来这确实有效,并且也将 OnAppearing 更改为异步 - 谢谢。
  • async 不是方法签名的一部分,async void 仅适用于事件处理程序:)
猜你喜欢
  • 1970-01-01
  • 2019-01-06
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 1970-01-01
相关资源
最近更新 更多