将异步轮询库更新为现代异步范例
Updating an asyncronous polling library to modern async paradigm
我有这个(有效的)异步轮询回调循环的基本实现:
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 异步模型,所以有什么 gotchas/pitfalls 需要注意吗?
这是我正在测试的更新版本,但与资源管理一样,多线程是您的代码似乎可以完美运行但实际上已严重损坏的领域,所以我很感激您的想法:
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");
}
unmanaged C++ doesn't know about the .Net async model, so are there any gotchas/pitfulls to watch out for?
就是那个。可以将 async
/await
应用于您的内部代码(例如,TaskLoop
),但您不能让它扩展到 COM 边界。所以Start
和Stop
不能做成async
.
切换到异步在您的代码中引入了一个错误。问题出在 async void Stop
方法上。它是从 Start
方法内部调用的,并且由于无法 await
和 async void
方法,因此两种方法同时执行了一段时间。所以下面两个命令先执行哪个就看运气了:
this.task = TaskLoopAsync(); // in Start method
task = null; // in Stop method
其他兴趣点:
CancellationTokenSource
未按预期方式使用。这个 class 不仅仅是美化的 volatile bool
。它还允许通过注册回调随时取消异步操作。例如,您可以通过其第二个可选参数传递令牌来立即取消异步 Task.Delay
:
await Task.Delay(1000, cancellation.Token);
那么你必须准备好处理令牌被取消时将抛出的OperationCanceledException
。这是通信的标准方式 cancellation in .NET,通过从一端抛出此异常并从另一端捕获它。
- 您可以在调用
Callback.SendMessage
方法之间实现更一致的间隔,方法是在调用该方法之前创建 Task.Delay
任务,然后等待它。
我有这个(有效的)异步轮询回调循环的基本实现:
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 异步模型,所以有什么 gotchas/pitfalls 需要注意吗?
这是我正在测试的更新版本,但与资源管理一样,多线程是您的代码似乎可以完美运行但实际上已严重损坏的领域,所以我很感激您的想法:
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");
}
unmanaged C++ doesn't know about the .Net async model, so are there any gotchas/pitfulls to watch out for?
就是那个。可以将 async
/await
应用于您的内部代码(例如,TaskLoop
),但您不能让它扩展到 COM 边界。所以Start
和Stop
不能做成async
.
切换到异步在您的代码中引入了一个错误。问题出在 async void Stop
方法上。它是从 Start
方法内部调用的,并且由于无法 await
和 async void
方法,因此两种方法同时执行了一段时间。所以下面两个命令先执行哪个就看运气了:
this.task = TaskLoopAsync(); // in Start method
task = null; // in Stop method
其他兴趣点:
CancellationTokenSource
未按预期方式使用。这个 class 不仅仅是美化的volatile bool
。它还允许通过注册回调随时取消异步操作。例如,您可以通过其第二个可选参数传递令牌来立即取消异步Task.Delay
:
await Task.Delay(1000, cancellation.Token);
那么你必须准备好处理令牌被取消时将抛出的OperationCanceledException
。这是通信的标准方式 cancellation in .NET,通过从一端抛出此异常并从另一端捕获它。
- 您可以在调用
Callback.SendMessage
方法之间实现更一致的间隔,方法是在调用该方法之前创建Task.Delay
任务,然后等待它。