暂停 Task.Factory.StartNew 中的任务
Posted
技术标签:
【中文标题】暂停 Task.Factory.StartNew 中的任务【英文标题】:Pausing a task within Task.Factory.StartNew 【发布时间】:2015-03-26 15:28:59 【问题描述】:在我的 Asp.Net MVC 5 项目中,我有一个大约 3 分钟的任务,我将其传递给 Task.Factory.StartNew()。
如果在任务中运行的代码的某个步骤中存在验证问题,我想从任务中暂停该任务。我不想异步延迟它,因为任务的其余部分将继续运行,这是不可能的。
由于我在任务中,我可以使用 thread.sleep() 而不产生任何影响吗?我读到我可能必须使用 TaskScheduler.Default 让 Task.Factory 为每个任务创建一个新线程。
我正在使用类似于 CancellationToken 的 PauseToken,因此我将能够根据用户输入恢复任务或取消此任务。
多线程真的让我害怕,我不想忽略一些东西。
这是一个 Thread.Sleep 实现的示例:
public void WaitIfPaused(PauseToken pauseToken, CancellationToken cancellationToken, IProgressBar progressBar)
//TODO: If paused for an hour notify user via noty and abort the task to keep it from completing by cancellation via cancellationToken.
//wait for 1 hour
for (int i = 0; i < 3600; i++)
ThrowExceptionIfCancelled(cancellationToken, progressBar);
if (pauseToken.IsPaused)
Thread.Sleep(1000);
else
break;
PauseToken:http://blogs.msdn.com/b/pfxteam/archive/2013/01/13/cooperatively-pausing-async-methods.aspx
请求:在共享代码库中实现任务结构。
public void StartTask(params object[] data)
//throw an exception if no ITask was found
if (_taskToRun == null)
throw new Exception("Task cannot be null");
//set up task cancellation
CancellationTokenSource = new CancellationTokenSource();
var cancellationToken = CancellationTokenSource.Token;
//set up task pausing
PauseTokenSource = new PauseTokenSource();
var pauseToken = PauseTokenSource.Token;
//start a new task using the Task that was set
_task = Task.Factory.StartNew(() => _taskToRun.Execute(cancellationToken, pauseToken, data), cancellationToken);
_taskToRun.Execute 调用的我的 Execute 方法:
Public override void Execute(CancellationToken cancellationToken, PauseToken pauseToken, params object[] data)
var managerList = (List<IFileManager>) data[0];
var instr = (List<InstructionSet>) data[1];
ProcessInstructions(managerList, instr, cancellationToken, pauseToken);
由于 cmets 的更新:
代码示例:3 条指令
For(var instruction in instructions)
instruction.Execute();
在我的执行方法中,我遇到了暂停场景并从执行中调用 WaitWhilePausedAsync。它将继续执行其他两条指令,但暂停当前唯一的指令执行方法。
编辑:通过等待 instruction.Execute() 它将等到 instruction.Execute() 完成或取消暂停。
最终编辑: 我能够通过等待 Execute 方法并像 Servy 和 I3arnon 建议的那样使其异步来解决问题。
最终代码示例:
foreach(var instruction in instructions)
try
await instruction.Execute(pauseToken);
catch(InvalidOperationException)
pauseTokenSource.IsPaused = true;
//ask if user wants to cancel or resume.
//Simplified
public async Task<bool> Execute(PauseToken pauseToken)
await pauseToken.WaitWhilePausedAsync();
//do work
【问题讨论】:
您链接到的模式似乎很适合您。为什么不使用它? 因此,您无需等待即可找到一种工具,该工具可以完全以尽可能有效的方式完成您想要的工作,然后您选择只是不使用它并在布尔值上旋转等待?只需拨打WaitWhilePausedAsync
和await
即可,就像博文中展示的那样。这正是它的初衷。
我不能使用 WaitWhilePausedAsync 因为我希望从内部暂停任务。例如
Execute
也应该是异步的,或者您只需要使用同步而不是异步的信号原语。如果你想使用同步方法来做同步的事情,你为什么要特意找到一个异步信号原语?跨度>
@BrandonTull:你真正想解决什么问题?对于 ASP.NET MVC 应用程序,StartNew
几乎可以肯定是错误的方法。
【参考方案1】:
您可以放心地使用Thread.Sleep
。唯一的缺点是线程会浪费在同步阻塞上。
您应该改用await Task.Delay(1000)
。该行之后的代码在等待完成之前不会执行,但在此期间您不会浪费线程:
public async Task WaitIfPausedAsync(PauseToken pauseToken, CancellationToken cancellationToken, IProgressBar progressBar)
for (int i = 0; i < 3600; i++)
ThrowExceptionIfCancelled(cancellationToken, progressBar);
if (pauseToken.IsPaused)
await Task.Delay(1000)
else
break;
编辑:我不知道PauseToken.WaitWhilePausedAsync
。您绝对应该使用它,而不是通过轮询 PauseToken.IsPaused
来复制自己
【讨论】:
或者他可以只是await WaitWhilePausedAsync
,这完全符合他的要求,不会等待超过所需的整秒,不会浪费线程在这个过程中,并且在语义上代表了确切的问题。您将竭尽全力不使用旨在解决手头确切问题的工具,而是使用更糟糕的解决方案。
我在原始问题的底部添加了为什么我无法在指令中执行异步暂停。Execute() 方法。
@Servy 当然.. 我不知道那种方法。
@BrandonTull 您需要将这些方法标记为async
和await
返回的Task
。这将使您能够在不阻塞线程的情况下进行顺序处理。
@BrandonTull 这并不意味着您不能使用异步。您似乎在混合同步和顺序。但是,如果您必须同步,请使用同步暂停令牌以上是关于暂停 Task.Factory.StartNew 中的任务的主要内容,如果未能解决你的问题,请参考以下文章
Task.Run 和Task.Factory.StartNew 区别
Task.Factory.StartNew 和 Task.Run 到底有什么区别?