任务同步的正确方法?
Correct Way to do Task Synchronization?
我正在做的 correct/best 方法是否可以做到这一点?
我有一个带计时器的window。每次计时器计时时,我都会调用如下所示的 RunTask
方法。在 RunTask
内,我调用 DoTheThing
。 DoTheThing
可能需要一段时间才能 运行,并且可能会失败(这是数据库更新)。我想确保在任何时候,我只有一个 DoTheThing
未完成。我还想确保我没有一堆 RunTask
实例都在排队等待 RunTask
实例释放锁 运行ning DoTheThing
.
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))
您可以重载它以采用异步 "action"... 实际上是 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;
}
}
}
我正在做的 correct/best 方法是否可以做到这一点?
我有一个带计时器的window。每次计时器计时时,我都会调用如下所示的 RunTask
方法。在 RunTask
内,我调用 DoTheThing
。 DoTheThing
可能需要一段时间才能 运行,并且可能会失败(这是数据库更新)。我想确保在任何时候,我只有一个 DoTheThing
未完成。我还想确保我没有一堆 RunTask
实例都在排队等待 RunTask
实例释放锁 运行ning DoTheThing
.
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))
您可以重载它以采用异步 "action"... 实际上是 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;
}
}
}