如何取消在C#中执行多个任务的等待任务

How to cancel await Task that perform multiple task in C#

我在空闲时间有一个功能,在某个时间间隔,例如每 30 秒,它将对硬件执行一次状态轮询。

方法名称是public static async Task PollCurrentHardwareStatus(),在这个方法中,它会检查每个硬件的状态,例如。我有 4 个设备来获取连接状态(打印机模块、现金模块、硬币模块、终端模块)。

如果public触摸主屏幕,它将转到下一页,我需要取消投票状态。如何在等待任务中取消设备的轮询状态?

我已经通过了 Cancel an Async Task or a List of Tasks (C#),但我似乎不知道将 CancellationToken 放在哪里。

我在 PollCurrentHardwareStatus 的代码:-

public static async Task PollCurrentHardwareStatus()
{
    try
    {
        //POLLING CARD READER
        if (GlobVars.HwIDTech.Enabled)
        {
            if (IDTechDevice.PingForReply())
            {
                LogEvents($"[App] Terminal OK.", EventLogEntryType.Information);
                AppDeviceStatus.strIDTechStatus = StatusMessageIDTech.strSuccessID;
            }
            else
            {
                LogEvents($"[App] IDTechDevice: Not found/Disconnected", EventLogEntryType.Information);
                AppDeviceStatus.strIDTechStatus = StatusMessageIDTech.strErrorID;
            }
        }
        //POLLING PRINTER
        if (GlobVars.HwCustom.Enabled)
        {
            string description = string.Empty;
            int status = 0;
            PrintMain.PrinterGetStatus(ref description, ref status);
            if (status == 0)
            {
                AppDeviceStatus.strPrinterStatus = StatusMessagePrinter.strSuccessID;
            }
            else
            {
                LogEvents($"[App] Printer error: {description}", EventLogEntryType.Information);
                AppDeviceStatus.strPrinterStatus = StatusMessagePrinter.strErrorID;
            }
        }
        //POLLING CASH COIN MODULE
        if (GlobVars.HwB2B.Enabled && GlobVars.HwBCR.Enabled)
        {
            string B2BStatus = await CCMain.GetCurrentDeviceStatus();
            if (B2BStatus == "DISABLED")
            {
                AppDeviceStatus.strB2BStatus = StatusMessageB2B.strSuccessID;
                LogEvents($"[App] Poll B2B device: Status - OK.", EventLogEntryType.Information);
            }
            else
            {
                LogEvents($"[App] Poll B2B device: Status - {B2BStatus}.", EventLogEntryType.Information);
                AppDeviceStatus.strB2BStatus = StatusMessageB2B.strErrorID;
            }

            if (ModuleCoins.OpenConnection())
            {
                await ModuleCoins.PerformSelfTest();
                AppDeviceStatus.strBCRStatus = StatusMessageBCR.strSuccessID;
            }
            else
            {
                AppDeviceStatus.strBCRStatus = StatusMessageBCR.strErrorID;
            }
        }
        UpdateErrorStatus();
    }
    catch (Exception ex)
    {
        LogEvents($"[App] Poll hardware status : Ex-{ex.Message}. Stack Trace-{ex.StackTrace}", EventLogEntryType.Error);
    }
    await Task.Delay(100);
}

我认为您可以通过调用 PollCurrentHardwareStatus() 的方法创建 CancellationTokenSource。请检查以下示例:

在 PollCurrentHardwareStatus 方法中添加 CancellationTokenSource 作为参数

public static async Task PollCurrentHardwareStatus(CancellationToken cts)
    {
        // your logic code
        // ...............
    }

创建一个 CancellationTokenSource 并在您的页面上调用它 class:

public class Page
{
    private CancellationTokenSource cancellationTokenSource;

    public Page()
    {
        cancellationTokenSource = new CancellationTokenSource();
    }

    public async void CallPoll()
    {
        await PollCurrentHardwareStatus(cancellationTokenSource.Token);
    }

    public void OnCancelPoll(object sender, EventArgs e)
    {
        cancellationTokenSource.Cancel();
    }
}

根据 MSDN:Cancellation in managed threads

Cancellation is cooperative and is not forced on the listener. The listener determines how to gracefully terminate in response to a cancellation request.

您必须创建 PollCurrentHardwareStatus 的重载,将 CancellationToken 对象作为输入。函数应该定期检查是否请求取消并优雅地取消函数。

这里面有几个问题:什么是reqularly?请求取消时该怎么办。

答案符合您的要求。这取决于中断的原因,您是否应该在 50 毫秒内取消,或者取消是否需要一秒钟。例如,如果您的流程在操作员第一次触摸您的屏幕时被中断,则该操作员可能愿意在屏幕响应之前等待半秒钟。但是,如果每次操作员键入一个字母时您的过程都被中断,那么一秒钟取消可能会很烦人。

因此,您应该多久检查一次取消的问题取决于使用情况。

async Task PollCurrentHardwareStatus(CancellatinToken token)
{
     token.ThrowIfCancellationRequested();
     DoSomeShortProcessing(); 
     token.ThrowIfCancellationRequested();
     DoSomeOtherProcessing();
     token.ThrowIfcancellationRequested();

等等

如果您调用一个处理时间较长的函数,就会出现问题。最好的办法是将 token 传递给另一个进程:

LongProcessingFunction(token);

其他函数应该定期检查令牌。 优雅地取消取决于此。 如果不能更改其他功能,则无法保证正确快速的取消。

async-await 在这方面帮不了你。由创建可等待函数的程序员提供接受 CancellationToken 的版本。

您会发现所有可等待的基本功能(读/写文件、从数据库或互联网获取信息等)都有一个接受 CancellationToken 的版本。

您可以启动一个线程并在请求取消时终止该线程,但这是相当危险的,因为您不知道线程终止时对象的状态。我不建议这样做。