首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如果需要,我如何创建一个可以取消自身和另一个任务的任务?

如果需要,我如何创建一个可以取消自身和另一个任务的任务?
EN

Stack Overflow用户
提问于 2022-04-25 20:59:04
回答 2查看 874关注 0票数 0

假设我有一个简单的UWP应用程序(因此没有与此情况无关的.NET 5或C# 8),有许多包含按钮的页面,所有这些页面都必须能够通过调用SeriousWorkAsyncFunWorkAsync开始工作。

代码语言:javascript
运行
复制
public async Task SeriousWorkAsync(SeriousObject obj)
{
    Setup(obj);
    for (int i = 0; i < 10000; i++)
    {
        await SeriousThingAsync(i);
    }
}

public async Task FunWorkAsync(FunObject obj)
{
    Setup(obj);
    for (int i = 0; i < 10000; i++)
    {
        await FunnyThingAsync(i);
    }
}

我的要求如下:

任何一个按钮都不能在任何时候被禁用。concurrently.

  • Whenever

  • 没有任何任务应该运行--我调用SeriousWorkAsync,我想要FunWorkAsync完成执行,在取消完成之后,SeriousWorkAsync应该执行,如果我调用SeriousWorkAsync而另一个调用正在执行,我必须取消另一个调用,而更新的调用只应该在取消完成之后执行。H 216H 117如果有额外的调用,那么第一个调用应该首先取消,只有最后一次调用才应该执行.

到目前为止,我能想到的最好的解决方案是在一个循环中延迟任务,直到另一个被取消,并在方法完成执行后立即设置几个布尔标志:

代码语言:javascript
运行
复制
private bool IsDoingWork = false;
private bool ShouldCancel = false;

public async Task FunWorkAsync(FunObject obj)
{
    CancelPendingWork();
    while (IsDoingWork)
    {
        await Task.Delay(30);
    }

    IsDoingWork = true;
    Setup(obj);
    for (int i = 0; i < 10000; i++)
    {
        if (ShouldCancel)
        {
            break;
        }
        await FunnyThingAsync(i);
    }

    IsDoingWork = false;
}

private void CancelPendingWork()
{
    if (IsDoingWork)
    {
        ShouldCancel = true;
    }
}

然而,这似乎是一个非常肮脏的解决办法,而且它不能满足我的最后一个需求。我知道我应该使用CancellationToken,但到目前为止,我使用它的尝试没有成功,即使经过了大量的搜索和头脑风暴。那我该怎么做呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-05-03 02:32:14

经过大量的搜索,我偶然发现了"A pattern for self-cancelling and restarting task“。这正是我所需要的,经过一些调整,我可以放心地说,我得到了我想要的。我的实施如下:

代码语言:javascript
运行
复制
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// The task that is currently pending.
/// </summary>
private Task _pendingTask = null;

/// <summary>
/// A linked token source to control Task execution.
/// </summary>
private CancellationTokenSource _tokenSource = null;

/// <summary>
/// Does some serious work.
/// </summary>
/// <exception cref="OperationCanceledException">Thrown when the
/// operation is cancelled.</exception>
public async Task SeriousWorkAsync(CancellationToken token)
{
    await CompletePendingAsync(token);
    this._pendingTask = SeriousImpl(this._tokenSource.Token);
    await this._pendingTask;
}

/// <summary>
/// Does some fun work.
/// </summary>
/// <exception cref="OperationCanceledException">Thrown when the
/// operation is cancelled.</exception>
public async Task FunWorkAsync(CancellationToken token)
{
    await CompletePendingAsync(token);
    this._pendingTask = FunImpl(this._tokenSource.Token);
    await this._pendingTask;
}

/// <summary>
/// Cancels the pending Task and waits for it to complete.
/// </summary>
/// <exception cref="OperationCanceledException">If the new token has
/// been canceled before the Task, an exception is thrown.</exception>
private async Task CompletePendingAsync(CancellationToken token)
{
    // Generate a new linked token
    var previousCts = this._tokenSource;
    var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
    this._tokenSource = newCts;

    if (previousCts != null)
    {
        // Cancel the previous session and wait for its termination
        previousCts.Cancel();
        try { await this._pendingTask; } catch { }
    }

    // We need to check if we've been canceled
    newCts.Token.ThrowIfCancellationRequested();
}

理想情况下,调用这些方法如下所示:

代码语言:javascript
运行
复制
try
{
    await SeriousWorkAsync(new CancellationToken());
}
catch (OperationCanceledException) { }

如果您愿意的话,您可以将方法包装在try catch中,并且总是生成一个新的令牌,这样消费者就不需要为取消应用特殊的处理:

代码语言:javascript
运行
复制
var token = new CancellationToken();
try
{
    await CompletePendingAsync(token);
    this._pendingTask = FunImpl(this._tokenSource.Token);
    await this._pendingTask;
}
catch { }

最后,我使用以下SeriousWorkAsyncFunWorkAsync实现进行了测试

代码语言:javascript
运行
复制
private async Task SeriousImpl(CancellationToken token)
{
    Debug.WriteLine("--- Doing serious stuff ---");
    for (int i = 1000; i <= 4000; i += 1000)
    {
        token.ThrowIfCancellationRequested();
        Debug.WriteLine("Sending mails for " + i + "ms...");
        await Task.Delay(i);
    }
    Debug.WriteLine("--- Done! ---");
}

private async Task FunImpl(CancellationToken token)
{
    Debug.WriteLine("--- Having fun! ---");
    for (int i = 1000; i <= 4000; i += 1000)
    {
        token.ThrowIfCancellationRequested();
        Debug.WriteLine("Laughing for " + i + "ms...");
        await Task.Delay(i);
    }
    Debug.WriteLine("--- Done! ---");
}
票数 1
EN

Stack Overflow用户

发布于 2022-04-25 21:17:17

因为您使用的是任务,并且需要等待任务的完成,所以可以使用此机制在下一次执行开始之前等待。

我没有测试这段代码,但它应该能工作。

代码语言:javascript
运行
复制
// Store current task for later
private Task CurrentTask = null;
// Create new cancellation token for cancelling the task
private CancellationTokenSource TokenSource = new CancellationTokenSource();
private object WorkLock = new object();

public async Task FunWorkAsync(FunObject obj)
{
    // Define the task we will be doing
    var task = new Task(async () =>
    {
        Setup(obj);
        for (int i = 0; i < 10000; i++)
        {
            // Break from the task when requested
            if (TokenSource.IsCancellationRequested)
            {
                break;
            }
            await FunnyThingAsync(i);
        }
    });
    
    // Make sure that we do not start multiple tasks at once
    lock (WorkLock)
    {
        if (CurrentTask != null)
        {
            TokenSource.Cancel();
            // You should make sure here that you can continue by providing cancellation token with a timeout
            CurrentTask.Wait(CancellationToken.None);
        }
        CurrentTask = task;
        // Restart cancelation token for new task
        TokenSource = new CancellationTokenSource();
        task.Start();
    }
    await task;

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72005686

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档