执行任务同步的正确方法
本文关键字:方法 同步 执行任务 | 更新日期: 2023-09-27 18:33:04
我在下面所做的是实现此目的的正确/最佳方法吗?
我有一个带计时器的窗口。 每次计时器滴答作响时,我都会调用如下所示的RunTask
方法。 在RunTask
,我打电话给DoTheThing
。 DoTheThing
可能需要一段时间才能运行,并且可能会失败(这是数据库更新)。 我想确保在任何时候,我只有一个DoTheThing
未完成。 我还想确保我没有一堆RunTask
实例全部排队并等待正在运行DoTheThing
的RunTask
实例释放锁。
public void RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
Task task = new Task(() => DoTheThing());
task.Start();
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
lock (this.runTaskLock)
{
this.isTaskRunning = false;
}
}
}
}
由于程序的体系结构,我宁愿将所有线程同步放在此方法中,而不是启用和禁用计时器。
通过稍微不同地思考问题,它变得容易得多。与其每 x
秒触发一次计时器,为什么不在调用之间等待 x
秒呢?
现在,您只需运行一个异步循环来执行计划的工作,并为自己节省一堆痛苦的同步工作。
async Task RunActionPeriodicallyAsync(Action action,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
action();
await Task.Delay(ts, token);
//or alternatively (see comment below)
//var delayTask = Task.Delay(ts, token);
//action();
//await delayTask;
}
}
现在,只需调用RunActionPeriodicallyAsync
一次,对其操作的调用将永远不会重叠。
RunActionPeriodicallyAsync(() => DoSomething(), TimeSpan.FromSeconds(10))
您可以重载它以采取异步"操作"......实际上是一个Func<Task>
...
async Task RunActionPeriodicallyAsync(Func<CancellationToken, Task> actionAsync,
TimeSpan ts,
CancellationToken token = default(CancellationToken))
{
while(!token.IsCancellationRequested)
{
await actionAsync(token);
await Task.Delay(ts, token);
//or alternatively (see comment below)
//await Task.WhenAll(actionAsync(token), Task.Delay(ts, token))
}
}
并使用它:
RunActionPeriodicallyAsync(async cancTok => await DoSomethingAsync(cancTok),
TimeSpan.FromSeconds(10))
如果您担心锁定过多,可以执行以下操作。如果一个任务完成而另一个任务只是在检查(标记),您可能会错过运行,但是您摆脱了一些锁定,并且只需要在设置isTaskRunnung = true
时锁定。此外,您需要将方法标记为异步,以便等待任务。
public async Task RunTask()
{
bool canRunTask = true;
// Check if another instance of this method is currently executing. If so, do not execute the rest of this method
if (this.isTaskRunning)
{ // <-- ___MARK___
canRunTask = false;
}
else
{
lock (this.runTaskLock)
{
if (this.isTaskRunning)
{
canRunTask = false;
}
else
{
this.isTaskRunning = true;
}
}
}
// Call DoTheThing if another instance is not currently outstanding
if (canRunTask)
{
try
{
await Task.Run(() => DoTheThing());
}
catch (Exception ex)
{
// Handle the exception
}
finally
{
this.isTaskRunning = false;
}
}
}