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

C# 将异步轮询库更新为现代异步范例,c#,.net,async-await,com,task-parallel-library,C#,.net,Async Await,Com,Task Parallel Library,我有一个异步轮询回调循环的(工作)基本实现: public void Start(ICallback callback) { if (Callback != null) Stop(); Console.WriteLine("STARTING"); Callback = callback; cancellation = new CancellationTokenSource(); this.task = Task.Run(() => T

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

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()
调用
Wait
?显然,
Task.Delay
Thread.Sleep
将被更改

我确信,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
/
wait
应用于内部代码(例如,
TaskLoop
)是可以的,但不能让它扩展到COM边界。因此,无法使
启动
停止
异步

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

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

task = null; // in Stop method
其他关注点:

  • CancellationTokenSource
    未按预期方式使用。这个类不仅仅是一个美化的
    易失性bool
    。它还允许通过注册回调随时取消异步操作。例如,您可以通过第二个可选参数传递令牌来立即取消异步:
  • 然后,您必须准备好处理取消令牌时将抛出的
    OperationCanceledException
    。这是标准的通信方式,从一端抛出此异常并从另一端捕获它

  • 通过在调用方法之前创建
    Task.Delay
    任务,然后等待,可以在调用
    Callback.SendMessage
    方法之间实现更一致的间隔

  • 无需“休眠/延迟”并在
    TaskLoop()
    中生成新任务。你可以直接调用
    SendMessage()。我想它是在我试图诊断的一些问题中出现的,之后就再也没有被移除过。但是如果我将
    Stop
    改为
    Wait Task
    而不是
    Task.Wait
    我肯定必须将其标记为
    async
    ?实际上,我已经测试了这些更改,它似乎可以工作,但多线程代码很容易“处理隐藏的bug”:)不,这是您想要避免的。不要使用
    async void
    。保持
    Stop
    同步。但是,如何保持同步呢?牺牲一点性能并使用
    等待
    ?考虑到它(看起来)的工作原理,有什么不对的地方吗?你能详细介绍一下吗?它基本上已经使用了
    Wait
    ,并且应该继续这样做。问题是它不会等待。因此,在
    stop
    返回时停止尚未完成,COM对象无法检测到这一点。
    await Task.Delay(1000, cancellation.Token);