在这种情况下,单个 CancellationTokenSource 如何适用于所有方法?

问题描述

我有一个 GUI 应用程序,其中断开连接按钮需要取消异步任务并关闭端口。 到目前为止,我可以通过在每个方法中使用 CancellationTokenSource 来实现。只有在任务取消后才能关闭端口,因此先取消令牌,然后在方法内部捕获 OperationCanceledException,进而关闭端口。

我一开始就声明:

private CancellationTokenSource my_cancelationTokenSource;
private CancellationTokenSource my_cancelationTokenSource_2;
private CancellationTokenSource my_cancelationTokenSource_3;

然后作为示例,我有以下三个方法 MyMethodAsync、MySecondMethodAsync 和 MyThirdMethodAsync:

private async Task MyMethodAsync(port)
{
    byte[] sent;

    my_cancelationTokenSource = new CancellationTokenSource();

    try
    {
        do
        {
            await Task.Delay(period,my_cancelationTokenSource.Token);
            //some code here..
            byte[] received = await message.SendReceive(sent,my_cancelationTokenSource.Token);
            //some code here..

        }
        while (true);
    }

    catch (OperationCanceledException)
    {
        try
        {
            my_cancelationTokenSource.dispose();
            my_cancelationTokenSource = null;

            if (port.IsOpen)
            {
                port.Close();
            }
        }
        catch { }
    }
}


private async Task MySecondMethodAsync(byte[] set)
{

    my_cancelationTokenSource_2 = new CancellationTokenSource();
   
   try
    {
            await Task.Delay(period,my_cancelationTokenSource_2.Token);
            //some code here
            bool check = await MyThirdMethod(set);
            //some code here
        }
    }
    catch (OperationCanceledException)
    {
        try
        {
            my_cancelationTokenSource_2.dispose();
            my_cancelationTokenSource_2 = null;

            if (port.IsOpen)
            {
                port.Close();
            }
        }
        catch { }
    }
}

private async Task MyThirdMethodAsync(byte[] set)
{

    my_cancelationTokenSource_3 = new CancellationTokenSource();
   
   try
    {
            await Task.Delay(period,my_cancelationTokenSource_3.Token);
            //some code here
            bool check = await MyThirdMethod(set);
            //some code here
        }
    }
    catch (OperationCanceledException)
    {
        try
        {
            my_cancelationTokenSource_3.dispose();
            my_cancelationTokenSource_3 = null;

            if (port.IsOpen)
            {
                port.Close();
            }
        }
        catch { }
    }
}

这里是断开连接按钮事件:

private void Button_disconnect_Click(object sender,RoutedEventArgs e)
{

    my_cancelationTokenSource?.Cancel();
    my_cancelationTokenSource_2?.Cancel();
    my_cancelationTokenSource_3?.Cancel();

}

我将有数十个这样的方法,通过这种方式,我必须为每个方法声明 CancellationTokenSource。如何修改代码以使所有方法一个取消令牌都适用于这种情况?只需为所有方法声明并使用 my_cancelationTokenSource。

解决方法

使用单个 CancellationTokenSource 并将 令牌 传递给每个方法。无论如何,您应该在任何异常之后清理端口,而不仅仅是取消,所以 finally 会很好。

您首先需要在调用方法的任何地方处理/忽略 OperationCancelledException。 (或者,如果调用者不需要知道取消,您可以在函数内部使用它 - 如果不知道这些是如何调用的,很难给出建议)

private async Task MyMethodAsync(port,CancellationToken cancelToken)
{
    byte[] sent;

    try
    {
        do
        {
            await Task.Delay(period,cancelToken);
            //some code here..
            byte[] received = await message.SendReceive(sent,cancelToken);
            //some code here..

        }
        while (true);
    }
    finally
    {
        try
        {
            if (port.IsOpen)
            {
                port.Close();
            }
        }
        catch { }
    }
}


private void Button_Disconnect_Click(object sender,RoutedEventArgs e)
{
    my_cancelationTokenSource.Cancel();
    my_cancelationTokenSource.Dispose();
}
,

我将有几十个这样的方法,通过这种方式,我必须为每个方法声明 CancellationTokenSource

我不确定操纵取消令牌的最佳方法是什么,但这听起来更像是 OOP 问题。您可以使用类来重用共享逻辑,而不是让多个方法共享相同的逻辑。例如,您可以有一个基类声明 CancellationTokenSource 的某个受保护字段、使用该字段的公共方法和执行特定工作的抽象方法。

    public abstract class MyAsyncWorkerBase
    {
        protected CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();

        public void Cancel()
        {
            _cancellationTokenSource.Cancel();
        }

        public async Task DoWork(IDontKnowTheType port)
        {
            try
            {
                await DoMySpecificWork(port);
            }
            catch (OperationCanceledException)
            {
                try
                {
                    _cancellationTokenSource.Dispose();
                    _cancellationTokenSource = null;

                    if (port.IsOpen)
                    {
                        port.Close();
                    }
                }
                catch { }
            }
        }

        protected abstract Task DoMySpecificWork(IDontKnowTheType port);
    }

然后你可以继承你的基类,然后在 DoMySpecificWork 中做你的事情。 我不知道你的代码库,所以我的例子有点模糊,你当然可以改进它。您也可以添加一些代码来处理多次调用 DoWork。 最后,为了方便起见,您可以将所有 MyAsyncWorkerBase 放入某个数组中,只需在 Button_Disconnect_Click 中遍历它们并在每个数组上调用 Cancel

,

您可以对所有方法使用单个 CancellationTokenSource。要关闭端口,您可以简单地 register 对取消令牌的回调,该回调将在令牌源被取消时收到通知。

_cancellationTokenSource.Token.Register(() => 
                               {
                                   if(port.IsOpen)
                                   {
                                       port.Close();
                                   }
                               });

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...