将异步轮询库更新为现代异步范例

问题描述 投票:0回答:2

我有这个(有效的)异步轮询回调循环的基本实现:

public void Start(ICallback callback)
{
    if (Callback != null)
        Stop();
    Console.WriteLine("STARTING");
    Callback = callback;
    cancellation = new CancellationTokenSource();
    this.task = Task.Run(() => TaskLoop(), cancellation.Token);
    Console.WriteLine("STARTED");
}

public void Stop()
{
    if (Callback == null)
    {
        Console.WriteLine("ALREADY stopped");
        return;
    }

    Console.WriteLine("STOPPING");
    cancellation.Cancel();
    try
    {
        task.Wait();
    }
    catch (Exception e)
    {
        Console.WriteLine($"{e.Message}");
    }
    finally
    {
        cancellation.Dispose();
        cancellation = null;
        Callback = null;
        task = null;
        Console.WriteLine("STOPPED");
    }
}

private void TaskLoop()
{
    int i = 0;
    while (!cancellation.IsCancellationRequested)
    {
        Thread.Sleep(1000);
        Console.WriteLine("Starting iteration... {0}", i);
        Task.Run(() =>
        {
            //just for testing
            Callback.SendMessage($"Iteration {i} at {System.DateTime.Now}");
        }).Wait();
        Console.WriteLine("...Ending iteration {0}", i++);
    }
    Console.WriteLine("CANCELLED");
}

实际上是通过COM从非托管C ++调用的,所以这是一个库项目(而回调是COM编组的对象),因此想先测试设计。

我正在切换为使用async范例,想知道它是否应该像在我的方法声明中撒些async灰尘并将Wait()调用交换为await一样简单?显然,Thread.Sleep将更改为Task.Delay

我相当确定,COM将为编组目的将一个线程专用于此对象,并且非托管C ++不了解.Net异步模型,因此是否有任何陷阱/陷阱值得关注?

这是我正在测试的更新版本,但是,像资源管理一样,多线程是您的代码似乎可以正常工作的地方,但实际上却严重损坏,因此,我不胜感激:

public void Start(ICallback callback)
{
    if (Callback != null)
        Stop();
    Console.WriteLine("STARTING");
    Callback = callback;
    cancellation = new CancellationTokenSource();
    this.task = TaskLoopAsync();
    Console.WriteLine("STARTED");
}

public async void Stop()
{
    if (Callback == null)
    {
        Console.WriteLine("ALREADY stopped");
        return;
    }

    Console.WriteLine("STOPPING");
    cancellation.Cancel();
    try
    {
        await task;
    }
    catch (Exception e)
    {
        Console.WriteLine($"{e.Message}");
    }
    finally
    {
        cancellation.Dispose();
        cancellation = null;
        Callback = null;
        task = null;
        Console.WriteLine("STOPPED");
    }
}

private async void TaskLoopAsync()
{
    int i = 0;
    while (!cancellation.IsCancellationRequested)
    {
        await Task.Delay(1000);
        Console.WriteLine("Starting iteration... {0}", i);
        Callback.SendMessage($"Iteration {i} at {System.DateTime.Now}");
        Console.WriteLine("...Ending iteration {0}", i++);
    }
    Console.WriteLine("CANCELLED");
}
c# .net async-await com task-parallel-library
2个回答
1
投票

非托管C ++不了解.Net异步模型,因此是否有需要注意的陷阱/陷阱?

只是那个。将async / await应用于内部代码(例如TaskLoop)很好,但是您不能让它扩展到COM边界。因此无法将StartStop设置为async


0
投票

切换为异步会在您的代码中引入错误。问题出在async void Stop方法上。它是从Start方法内部调用的,并且由于无法访问awaitasync void方法,因此这两种方法会同时执行一段时间。因此,幸运的是,以下两个命令中的哪一个将首先执行:

async void

其他景点:

  1. this.task = TaskLoopAsync(); // in Start method task = null; // in Stop method 未按预期使用。此类不仅仅是荣耀的CancellationTokenSource。它还允许通过注册回调随时取消异步操作。例如,您可以通过将令牌传递到其第二个可选参数来立即取消异步volatile bool
Task.Delay

然后,您必须准备好处理取消令牌时将抛出的Task.Delay。通过从一端抛出此异常并从另一端捕获此异常,这是通信await Task.Delay(1000, cancellation.Token); 的标准方法。

  1. 通过在调用方法之前创建OperationCanceledException任务,然后再等待它,可以在调用cancellation in .NET方法之间获得更一致的间隔。
© www.soinside.com 2019 - 2024. All rights reserved.