我需要从C#Web应用程序向服务发布一些数据。用户使用应用程序时会收集数据本身(一种使用情况统计信息)。我不希望在每个用户的请求期间向服务发送数据,我宁愿在应用程序中收集数据,然后在一个单独的线程中发送单个请求中的所有数据,这不会满足用户的请求(我的意思是用户不必等待服务处理请求)。为此,我需要一种JS的setInterval
模拟 - 每隔X秒启动一次函数,将所有收集的数据刷新到服务中。
我发现Timer
类提供了一些类似的(Elapsed
事件)。但是,这允许只运行一次方法,但这不是一个大问题。它的主要困难是它需要签名
void MethodName(object e, ElapsedEventArgs args)
虽然我想启动异步方法,但会调用web服务(输入参数并不重要):
async Task MethodName(object e, ElapsedEventArgs args)
有人可以建议如何解决所描述的任务吗?任何提示赞赏。
答案 0 :(得分:21)
async
等效项是while
循环Task.Delay
(内部使用System.Threading.Timer
):
public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken)
{
while (true)
{
await FooAsync();
await Task.Delay(interval, cancellationToken)
}
}
传递CancellationToken
非常重要,这样您就可以在需要时停止该操作(例如关闭应用程序时)。
现在,虽然这通常与.Net相关,但在ASP.Net中,任何类型的火灾都会发生危险而忘记。有几个解决方案(如HangFire),Fire and Forget on ASP.NET by Stephen Cleary中的How to run Background Tasks in ASP.NET by Scott Hanselman其他人记录了一些
答案 1 :(得分:4)
这样做的简单方法是使用Tasks和一个简单的循环:
public async Task StartTimer(CancellationToken cancellationToken)
{
await Task.Run(async () =>
{
while (true)
{
DoSomething();
await Task.Delay(10000, cancellationToken);
if (cancellationToken.IsCancellationRequested)
break;
}
});
}
当您想要停止线程时,只需中止令牌:
cancellationToken.Cancel();
答案 2 :(得分:2)
以下是一种以周期性方式调用异步方法的方法:
public static async Task PeriodicAsync(Func<Task> taskFactory, TimeSpan interval,
CancellationToken cancellationToken = default)
{
while (true)
{
var delayTask = Task.Delay(interval, cancellationToken);
await taskFactory();
await delayTask;
}
}
所提供的taskFactory
每interval
被调用,然后等待创建的Task
。等待的持续时间不会影响间隔,除非间隔时间更长。在这种情况下,no-overlaping-execution的主体优先,因此将延长等待时间以匹配等待时间。
在发生异常的情况下,PeriodicAsync
任务将失败,因此,如果您希望它具有弹性,则应该在taskFactory
内包含严格的错误处理。
用法示例:
Task statisticsUploader = PeriodicAsync(async () =>
{
try
{
await UploadStatisticsAsync();
}
catch (Exception ex)
{
// Log the exception
}
}, TimeSpan.FromMinutes(5));