如何在C#Async/Await应用程序中创建循环服务?
我用一个方法编写了一个类,该方法在线程池中作为长期运行的任务运行。该方法是一种监视服务,用于定期发出REST请求以检查另一个系统的状态。它只是一个while()循环,内部有一个try()catch(),这样它就可以处理自己的异常,并在发生意外情况时优雅地继续 下面是一个例子:如何在C#Async/Await应用程序中创建循环服务?,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我用一个方法编写了一个类,该方法在线程池中作为长期运行的任务运行。该方法是一种监视服务,用于定期发出REST请求以检查另一个系统的状态。它只是一个while()循环,内部有一个try()catch(),这样它就可以处理自己的异常,并在发生意外情况时优雅地继续 下面是一个例子: public void LaunchMonitorThread() { Task.Run(() => { while (true) { try
public void LaunchMonitorThread()
{
Task.Run(() =>
{
while (true)
{
try
{
//Check system status
Thread.Sleep(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
});
}
它工作得很好,但我想知道是否有另一种模式可以让Monitor方法作为标准Async/Wait应用程序的常规部分运行,而不是使用Task.run()启动它——基本上我是在尝试避免使用fire and forget模式
因此,我尝试将代码重构为:
public async Task LaunchMonitorThread()
{
while (true)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
但是当我尝试在另一个异步方法中调用该方法时,我得到了有趣的编译器警告:
由于此调用未被等待,因此当前方法的执行将在调用完成之前继续
现在我认为这是正确的,也是我想要的。但我有疑问,因为我是async/Wait新手这段代码是否会按我预期的方式运行,还是会死锁或执行其他致命的操作?您真正想要的是使用。使用名称空间中的名称。无需使用
任务
或其任何其他变体(对于您显示的代码示例)
从
提供以指定间隔在线程池线程上执行方法的机制
您也可以使用,但可能不需要它。有关这两个计时器之间的比较,请参见。如果需要启动并忘记操作,则可以。我建议使用
CancellationToken
public async Task LaunchMonitorThread(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
//Check system status
//Use task.delay instead of thread.sleep:
await Task.Delay(5000, token);
}
catch (Exception e)
{
Console.WriteLine("An error occurred. Resuming on next loop...");
}
}
}
除此之外,你可以像这样使用它
var cancellationToken = new CancellationToken();
var monitorTask = LaunchMonitorThread(cancellationToken);
并将task和/或cancellationToken保存到您想要的任何位置中断监视器用于激发的方法
task.Run
非常适合从非异步方法启动长时间运行的异步函数
你是对的:忘记部分是不正确的。例如,如果您的进程将要关闭,那么如果您善意地要求已启动的线程完成其任务,将会更加整洁
正确的方法是使用CancellationTokenSource
。如果您将CancellationTokenSource
订购为Cancel
,则从该CancellationTokenSource
开始使用令牌的所有过程将在合理时间内整齐停止
因此,让我们创建一个类LongRunningTask
,该类将在构造时创建一个长时间运行的Task
,并在Dispose()时使用CancellationTokenSource
来取消该任务
由于CancellationTokenSource
与任务
实现IDisposable
一样,当LongRunningTask
对象被释放时,最好的方法是Dispose
这两个
class LongRunningTask : IDisposable
{
public LongRunningTask(Action<CancellationToken> action)
{ // Starts a Task that will perform the action
this.cancellationTokenSource = new CancellationTokenSource();
this.longRunningTask = Task.Run( () => action (this.cancellationTokenSource.Token));
}
private readonly CancellationTokenSource cancellationTokenSource;
private readonly Task longRunningTask;
private bool isDisposed = false;
public async Task CancelAsync()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
await this.longRunningTask;
}
// for completeness a non-async version:
public void Cancel()
{ // cancel the task and wait until the task is completed:
if (this.isDisposed) throw new ObjectDisposedException();
this.cancellationTokenSource.Cancel();
this.longRunningTask.Wait;
}
}
用法:
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
动作{…}
有一个CancellationToken
作为输入参数,您的函数应该定期检查它
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}
您可以调用Token.ThrowIfCancellationRequested
,而不是检查令牌。这将抛出一个异常,您必须捕获它。这可能会有所帮助:这不是仍然会触发并忘记吗?这不是你基本上想要的吗?如果你在不等待的情况下调用这个任务返回方法,你仍然在做fire And forget。这就是编译器警告的内容。主要区别在于,在第二个示例中,监视逻辑将在主上下文/线程上调度,而第一个示例将使其在线程池上运行。它不应该阻塞线程,尽管这非常有用。非常感谢。
var longRunningTask = new LongRunningTask( (token) => MyFunction(token)
...
// when application closes:
await longRunningTask.CancelAsync(); // not necessary but the neat way to do
longRunningTask.Dispose();
async Task MyFunction(CancellationToken token)
{
while (!token.IsCancellationrequested)
{
// do what you have to do, make sure to regularly (every second?) check the token
// when calling other tasks: pass the token
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
}