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