【问题标题】:Updating an asyncronous polling library to modern async paradigm将异步轮询库更新为现代异步范例
【发布时间】:2020-10-02 06:22:16
【问题描述】:

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

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

【问题讨论】:

  • 无需“休眠/延迟”并在TaskLoop() 中生成新任务。您可以直接拨打SendMessage()
  • @Nick 是的,我想知道为什么我也添加了这个。我认为是在我试图诊断的一些问题期间,它从未被删除。

标签: c# .net async-await com task-parallel-library


【解决方案1】:

非托管 C++ 不了解 .Net 异步模型,所以有什么需要注意的陷阱/坑吗?

就是那个。可以将async/await 应用于您的内部代码(例如,TaskLoop),但不能让它扩展到 COM 边界。所以StartStop不能变成async

【讨论】:

  • 嗯。但是,如果我将Stop 更改为await Task 而不是Task.Wait,我肯定必须 将其标记为async?我实际上已经测试了这些更改,它似乎可以工作,但是多线程代码很容易“处理隐藏的错误”:)
  • 不,那是你想要避免的。不要使用async void。保持Stop 同步。
  • 但是,怎么做?牺牲一点性能并使用Wait?鉴于它(似乎)有效,出了什么问题 - 你能详细说明一下吗?
  • 它基本上已经使用了Wait,并且应该继续这样做。 problem with async void 是它不等待。所以在Stop 返回时停止还没有完成,COM 对象无法检测到。
【解决方案2】:

切换到异步在您的代码中引入了一个错误。问题在于async void Stop 方法。它是从Start 方法内部调用的,由于无法将awaitasync void 方法联系起来,因此这两个方法会同时执行一段时间。因此,下面两个命令中的哪一个将首先执行是一个运气问题:

this.task = TaskLoopAsync(); // in Start method

task = null; // in Stop method

其他兴趣点:

  1. CancellationTokenSource 未按预期方式使用。这个类不仅仅是一个美化的volatile bool。它还允许通过注册回调随时取消异步操作。例如,您可以通过第二个可选参数传递令牌来立即取消异步 Task.Delay
await Task.Delay(1000, cancellation.Token);

那么你必须准备好处理取消令牌时将抛出的OperationCanceledException。这是与cancellation in .NET 通信的标准方式,即从一端抛出此异常并从另一端捕获它。

  1. 您可以在调用Callback.SendMessage 方法之间实现更一致的间隔,方法是在调用方法之前创建Task.Delay 任务,然后等待它。

【讨论】:

    猜你喜欢
    • 2011-12-26
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 2017-05-19
    • 1970-01-01
    相关资源
    最近更新 更多