我有一个班级Worker
正在做一些工作(模拟工作量):
public class Worker
{ ...
public void DoWork(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(2000);
}
}
现在我想在Task.Run
(来自我的Windows窗体应用程序,按下按钮)中使用此方法,可以取消:
private CancellationTokenSource _ctSource;
try
{
Task.Run(() =>
{
_worker.DoWork(_ctSource.Token);
},_ctSource.Token);
}
catch (AggregateException aex)
{
String g = aex.Message;
}
catch (OperationCanceledException ex)
{
String g = ex.Message;
}
catch (Exception ex)
{
String g = ex.Message;
}
但是当任务开始时,我无法用_ctSource.Cancel()
取消它;
我在visual studio中收到错误,OperationCanceledException
未处理!
但我在try-catch子句中包围了Task.Run调用! Worker
对象中出现的异常应该抛出还是不抛出?
有什么问题?
答案 0 :(得分:2)
您的Task.Run
来电会创建任务,然后立即返回。它永远不会抛出。但它创建的任务可能会失败或被取消稍后。
这里有几个解决方案:
使用await
:
await Task.Run(...)
根据失败/取消情况附加续篇:
var task = Task.Run(...);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => ..., TaskContinuationOptions.OnlyOnFaulted);
在失败时附加一个延续:
Task.Run(...).ContinueWith(t => ..., TaskContinuationOptions.NotOnRanToCompletion);
您可以/应该使用的解决方案取决于周围的代码。
答案 1 :(得分:0)
您需要更新令牌
private CancellationTokenSource _ctSource = new CancellationTokenSource();
为什么要在DoWork中投入期望?
一个线程的异常不会冒泡另一个启动该线程的线程。
答案 2 :(得分:0)
如果并行任务抛出异常,它将返回执行并将拥有它的Exception
属性(作为AggregateException
,您应该检查其InnerException
)set(及其IsCanceled
或IsFaulted
属性设置为true
)。我的项目中的一些最小示例代码将异常升级为主线程:
var t = new Task(Initialize);
t.Start();
while (!t.IsCompleted && !t.IsFaulted)
{
// Do other work in the main thread
}
if (t.IsFaulted)
{
if (t.Exception != null)
{
if(t.Exception.InnerException != null)
throw t.Exception.InnerException;
}
throw new InvalidAsynchronousStateException("Initialization failed for an unknown reason");
}
如果您使用CancellationTokenSource
,则应该很容易对其进行增强,以检查IsCanceled
(而不是IsFaulted
)
你也可以在我的项目中使用Task.Wait()
而不是while
循环......在这种精确的情况下,使用while循环似乎更合适,但你需要等待{ {1}}以这种或那种方式结束。
如果您使用Task
,则可以使用Task.Run()
传入原始任务(您可以在其中检查.ContinueWith(Task)
或IsFaulted
),或拥有它根据您的遗嘱,仅在故障执行时运行。