正确取消异步操作,并再次火了 [英] Correctly cancel async operation and fire it again

查看:168
本文介绍了正确取消异步操作,并再次火了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何处理的情况下,在这里用户可以点击按钮,这将调用长时间运行的异步操作,多个时间。

How to handle case, where user might hit the button, which invokes long running async operation, multiple time.

我的想法是首先检查,如果异步操作运行,取消它,再次触发了。

My idea was first check if the async operation is running, cancel it and fire it again.

到目前为止,我已经试图建立这种使用CancellationTokenSource功能,但预期它不工作。有些时候,有运行两个异步操作,所以老字号异步算子的研究,当我开始新的不取消还没有这拌和resul处理。

So far I have tried to build this kind of functionality using CancellationTokenSource, but it is not working as expected. Some times there is two async operations running, so the "old" async oprations is not cancelled yet when I start new one and this mixes up the resul handling.

任何建议或实例如何处理这种情况?

Any suggestions or examples how to handle this kind of case?

public async void Draw()
{
    bool result = false;

    if (this.cts == null)
    {
        this.cts = new CancellationTokenSource();

        try
        {
            result = await this.DrawContent(this.TimePeriod, this.cts.Token);
        }
        catch (Exception ex)
        {}
        finally
        {
            this.cts = null;
        }
    }

    else
    {
        this.cts.Cancel();
        this.cts = new CancellationTokenSource();

        try
        {
            result = await this.DrawContent(this.TimePeriod, this.cts.Token);
        }
        catch (Exception ex)
        {}
        finally
        {
            this.cts = null;
        }
    }

}

编辑:
最后,我认为这是不坏,有在较短的时间(当新的被激发,但旧的还没有被取消)运行两个异步操作。

In the end, I think it is not bad that there is two async operations running in short time (when the new is fired but the old one is not yet cancelled).

在这里真正的问题是我如何显示最终用户的进展情况。当旧的异步操作结束,它隐藏进度指示器从最终用户,但新近解雇异步操作仍在运行。

The real problem here is how I display the progress for enduser. As when the old async operation ends, it hides the progress indicator from enduser but the newly fired async operation is still running.

EDIT2:
内部DrawContent(......)我用ThrowIfCancellationRequested,所以取消正在运行的任务似乎工作正常。

Inside DrawContent(...) I use ThrowIfCancellationRequested, so cancelling the running task seems to work ok.

关于进度显示。当抽奖()被调用时,我设置负荷指标可见,当这个方法结束后,我隐藏加载指示器。所以,现在当我后开始新的一个previous异步操作将被取消,我的加载指示灯设置隐藏。我应该如何追踪是否有当老一结束另一个异步方法仍在运行。

About progress display. When the Draw() is called, I set loading indicator visible and when this method ends, I hide loading indicator. So now when the previous async operation is cancelled after I start new one, my loading indicator is set hidden. How should I keep track if there is another async method still running when the "old" one ends.

推荐答案

我想借此机会完善一些相关的$ C $ç。你的情况,它可以用来像下面。

I'd like to take a chance to refine some related code. In your case, it can be used like below.

请注意,如果待处理操作的previous实例是失败(抛出比 OperationCanceledException 其他任何东西),你还是会看到它的错误信息。此行为可以容易地改变。

Note, if the previous instance of the pending operation has failed (thrown anything other than OperationCanceledException), you'll still see an error message for it. This behavior can be easily changed.

它只是隐藏进度UI如果它是否仍然是最新的任务实例的操作结束:如果(thisTask == _draw.PendingTask)_progressWindow.Hide();

It only hides the progress UI if by the end of the operation if it's still the most recent instance of the task: if (thisTask == _draw.PendingTask) _progressWindow.Hide();

这code是不是线程安全的,是( _draw.RunAsync 不能同时调用),并且被设计成从UI线程调用。

This code is not thread-safe as is (_draw.RunAsync can't be called concurrently), and is designed to be called from a UI thread.

Window _progressWindow = new Window();

AsyncOp _draw = new AsyncOp();

async void Button_Click(object s, EventArgs args)
{
    try
    {
        Task thisTask = null;
        thisTask = _draw.RunAsync(async (token) =>
        {
            var progress = new Progress<int>(
                (i) => { /* update the progress inside progressWindow */ });

            // show and reset the progress
            _progressWindow.Show();
            try
            {
                // do the long-running task
                await this.DrawContent(this.TimePeriod, progress, token);
            }
            finally
            {
                // if we're still the current task,
                // hide the progress 
                if (thisTask == _draw.PendingTask)
                    _progressWindow.Hide();
            }
        }, CancellationToken.None);
        await thisTask;
    }
    catch (Exception ex)
    {
        while (ex is AggregateException)
            ex = ex.InnerException;
        if (!(ex is OperationCanceledException))
            MessageBox.Show(ex.Message);
    }
}

class AsyncOp
{
    Task _pendingTask = null;
    CancellationTokenSource _pendingCts = null;

    public Task PendingTask { get { return _pendingTask; } }

    public void Cancel()
    {
        if (_pendingTask != null && !_pendingTask.IsCompleted)
            _pendingCts.Cancel();
    }

    public Task RunAsync(Func<CancellationToken, Task> routine, CancellationToken token)
    {
        var oldTask = _pendingTask;
        var oldCts = _pendingCts;

        var thisCts = CancellationTokenSource.CreateLinkedTokenSource(token);

        Func<Task> startAsync = async () =>
        {
            // await the old task
            if (oldTask != null && !oldTask.IsCompleted)
            {
                oldCts.Cancel();
                try
                {
                    await oldTask;
                }
                catch (Exception ex)
                {
                    while (ex is AggregateException)
                        ex = ex.InnerException;
                    if (!(ex is OperationCanceledException))
                        throw;
                }
            }
            // run and await this task
            await routine(thisCts.Token);
        };

        _pendingCts = thisCts;

        _pendingTask = Task.Factory.StartNew(
            startAsync,
            _pendingCts.Token,
            TaskCreationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext()).Unwrap();

        return _pendingTask;
    }
}

这篇关于正确取消异步操作,并再次火了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆