Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 正确地取消异步操作并再次激发它_C#_Asynchronous_Windows Phone 8_Async Await_Cancellationtokensource - Fatal编程技术网

C# 正确地取消异步操作并再次激发它

C# 正确地取消异步操作并再次激发它,c#,asynchronous,windows-phone-8,async-await,cancellationtokensource,C#,Asynchronous,Windows Phone 8,Async Await,Cancellationtokensource,如何处理这种情况,用户可能多次点击按钮,调用长时间运行的异步操作 我的想法是首先检查异步操作是否正在运行,取消它并再次启动它 到目前为止,我已经尝试使用CancellationTokenSource构建这种功能,但它没有按预期工作。有时会有两个异步操作在运行,所以当我启动新的异步操作时,“旧的”异步操作还没有取消,这会混淆结果处理 如何处理此类案件,有什么建议或例子吗 public async void Draw() { bool result = false; if (thi

如何处理这种情况,用户可能多次点击按钮,调用长时间运行的异步操作

我的想法是首先检查异步操作是否正在运行,取消它并再次启动它

到目前为止,我已经尝试使用CancellationTokenSource构建这种功能,但它没有按预期工作。有时会有两个异步操作在运行,所以当我启动新的异步操作时,“旧的”异步操作还没有取消,这会混淆结果处理

如何处理此类案件,有什么建议或例子吗

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

}
编辑: 最后,我认为有两个异步操作在短时间内运行(当新操作启动但旧操作尚未取消时)并不坏

这里真正的问题是如何为最终用户显示进度。当旧的异步操作结束时,它会对最终用户隐藏进度指示器,但新启动的异步操作仍在运行

编辑2:
在DrawContent(…)中,我使用ThrowIfCancellationRequested,因此取消正在运行的任务似乎可以


关于进度显示。调用Draw()时,我将加载指示器设置为可见,当此方法结束时,我将隐藏加载指示器。所以现在,当我启动新的异步操作后取消上一个异步操作时,我的加载指示器被设置为隐藏。当“旧”异步方法结束时,如果还有另一个异步方法仍在运行,我该如何跟踪它。

我想借此机会改进一下。在您的情况下,它可以像下面这样使用

注意,如果挂起操作的上一个实例失败(抛出除
OperationCanceledException
之外的任何内容),您仍然会看到它的错误消息。这种行为很容易改变

只有在操作结束时仍然是任务的最新实例时,它才会隐藏进度UI:
if(thistTask==\u draw.PendingTask)\u progressWindow.Hide()

此代码不是线程安全的(
\u draw.RunAsync
不能被并发调用),并且设计为从UI线程调用

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;
    }
}
Window\u progressWindow=newwindow();
AsyncOp_draw=new AsyncOp();
异步无效按钮\u单击(对象、事件参数)
{
尝试
{
Task thisTask=null;
ThistTask=\u draw.RunAsync(异步(令牌)=>
{
var progress=新的进度(
(i) =>{/*更新progressWindow内的进度*/});
//显示并重置进度
_progressWindow.Show();
尝试
{
//执行长时间运行的任务
等待.DrawContent(this.TimePeriod,progress,token);
}
最后
{
//如果我们仍然是当前的任务,
//隐瞒进展
如果(此任务==\u draw.PendingTask)
_progressWindow.Hide();
}
},CancellationToken.None);
等待这项任务;
}
捕获(例外情况除外)
{
while(ex是aggregateeexception)
ex=ex.InnerException;
如果(!(ex是OperationCanceledException))
MessageBox.Show(例如Message);
}
}
类异步操作
{
任务_pendingTask=null;
CancellationTokenSource _pendingCts=null;
公共任务PendingTask{get{return{U PendingTask;}}
公开作废取消()
{
如果(_pendingTask!=null&!_pendingTask.IsCompleted)
_pendingCts.Cancel();
}
公共任务RunAsync(Func例程,CancellationToken令牌)
{
var oldTask=_pendingTask;
var oldCts=_pendingCts;
var thisCts=CancellationTokenSource.CreateLinkedTokenSource(令牌);
Func startAsync=async()=>
{
//等待旧任务
if(oldTask!=null&&!oldTask.IsCompleted)
{
oldCts.Cancel();
尝试
{
等待旧任务;
}
捕获(例外情况除外)
{
while(ex是aggregateeexception)
ex=ex.InnerException;
如果(!(ex是OperationCanceledException))
投掷;
}
}
//运行并等待此任务
等待例程(thisCts.Token);
};
_pendingCts=此Cts;
_pendingTask=Task.Factory.StartNew(
startAsync,
_挂起标记,
任务创建选项。无,
TaskScheduler.FromCurrentSynchronizationContext()).Unwrap();
返回待处理任务;
}
}
调用cts.Cancel()不会自动停止任务。您的任务需要主动检查是否已请求取消。您可以这样做:

public async Task DoStuffForALongTime(CancellationToken ct)
{
    while (someCondition)
    {
        if (ct.IsCancellationRequested)
        {
            return;
        }

        DoSomeStuff();
    }
}

为什么不遵循BackgroundWorker模式,在DrawContent中打破循环

private bool _cancelation_pennding=false;
private delegate DrawContentHandler(TimePeriod period, Token token)
private DrawContentHandler _dc_handler=null;

.ctor(){
    this._dc_handler=new DrawContentHandler(this.DrawContent)
}
public void CancelAsync(){
    this._cancelation_pennding=true;
}
public void Draw(){
    this._dc_handler.BeginInvoke(this.TimePeriod, this.cts.Token)
}
private void DrawContent(TimePeriod period, Token token){
    loop(){
        if(this._cancelation_pennding)
        {
            break;
        }

        //DrawContent code here
    }
    this._cancelation_pennding=false;
}

它怎么不起作用。不要只是说它不起作用。当然,接受所有例外通常是个坏主意。不要那样做。当你这么做的时候,你永远都不会知道出了什么问题。当然,旧的那一个仍然可以运行。在任何时候都不会等到上一个操作完成后再继续。如果在请求取消和任务完成之间有一段时间,那么这段时间将有两个作业在运行。@devha,这取决于您如何显示进度。这是一个对话框吗?再次更新问题:)@devha,更新了答案。这如何帮助确保任务的上一个实例已完全取消,并且与新实例不重叠?我使用ThrowIfCancellationRequested取消异步操作。