我正在尝试处理System.Threading.Tasks.Task
中的异常我之前没有使用过这些,似乎误解了ContinueWith
的工作方式;因此我的ContinueWith
在错误的时间开火。
鉴于以下内容; workers
只是我长期运行流程的列表。
......
workers.Add(new Workers.Tests.TestWorker1());
workers.Add(new Workers.Tests.TestWorker2());
// Start all the workers.
workers.ForEach(worker =>
{
// worker.Start schedules a timer and calls DoWork in the worker
System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(worker.Start);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
})
.....
我的处理程序方法是
private void ExceptionHandler(System.Threading.Tasks.Task arg1, object arg2)
{
DebugLogger.Write("uh oh.. it died");
}
我的TestWorker
是:
class TestWorker1 : Worker
{
int count = 1;
public override void DoWork(object timerState)
{
DebugLogger.Write(string.Format("{0} ran {1} times", workerName, count));
count++;
ScheduleTimer();
}
}
并且
class TestWorker2 : Worker
{
int count = 1;
public override void DoWork(object timerState)
{
DebugLogger.Write(string.Format("{0} ran {1} times", workerName, count));
count++;
if (count == 3)
throw new Exception("I'm going to die....");
ScheduleTimer();
}
}
ScheduleTimer()
只是设置运行DoWork的时间间隔
会发生什么......
调试时,会创建并启动所有任务。一旦DoWork
第一次调用ScheduleTimer()
,我的ExceptionHandler就会被击中;如截图所示 - 这对两个工人都有效。
当在TestWorker2
中遇到异常时,调试器将不会从那里继续 - 因为我按下继续,希望点击我的ExceptionHandler
,调试器只是继续抛出异常。
我希望实现的目标
我希望我的ExceptionHandler
仅在抛出正在运行的任务中的异常时触发。我发现我进入ExceptionHandler
的唯一时间就是它运行的时间,而我的实际异常只会保持循环。
我缺少什么?
根据评论,这是主Worker
public abstract class Worker : IDisposable
{
internal string workerName;
internal Timer scheduler;
internal DateTime scheduledTime;
public Worker()
{
string t = this.GetType().ToString();
workerName = t.Substring(t.LastIndexOf(".") + 1).AddSpacesBeforeUppercase(true).Trim();
}
/// <summary>
/// Set to true when the worker is performing its task, false when its complete
/// </summary>
public bool IsCurrentlyProcessing { get; set; }
public void Start()
{
DebugLogger.Write(workerName + " Started");
ScheduleTimer();
}
/// <summary>
/// default functionality for setting up the timer.
/// Typically, the timer will fire in 60 second intervals
/// Override this method in child classes for different functionality
/// </summary>
public virtual void ScheduleTimer()
{
scheduler = new Timer(new TimerCallback(DoWork));
int interval = 60;
int.TryParse(ConfigurationManager.AppSettings[string.Format("{0}{1}", workerName.Replace(" ", ""), "Interval")], out interval);
scheduledTime = DateTime.Now.AddSeconds(interval);
if (DateTime.Now > scheduledTime)
scheduledTime = scheduledTime.AddSeconds(interval);
int dueTime = Convert.ToInt32(scheduledTime.Subtract(DateTime.Now).TotalMilliseconds);
scheduler.Change(dueTime, Timeout.Infinite);
}
public abstract void DoWork(object timerState);
public void Stop()
{
// kill stuff
if (scheduler != null)
scheduler.Dispose();
DebugLogger.Write(workerName + " stop");
this.Dispose();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
if (disposing)
{
// any specific cleanup
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
答案 0 :(得分:4)
从屏幕截图中可以看出arg2
是您的TaskContinuationOptions.OnlyOnFaulted
对象,这是出错的最大线索。由于您传递了Action<Task, Object>
,因此使用ContinueWith
的{{3}}重载,这会导致您的延续选项作为state
参数传入。
将ExceptionHandler
更改为
private void ExceptionHandler(System.Threading.Tasks.Task arg1)
{
DebugLogger.Write("uh oh.. it died");
}
因此您将使用Task.ContinueWith Method (Action<Task, Object>, Object)
重载,或者您可以将通话更改为
task.ContinueWith(ExceptionHandler, null, TaskContinuationOptions.OnlyOnFaulted);
这样您就可以开始使用Task.ContinueWith(Action<Task>, TaskContinuationOptions)
重载。
答案 1 :(得分:0)
您的日志记录组件可能不支持多个并发写入。
如果您有可能,我建议您将代码重构为async
/ await
模式,它将更具可读性。
假设您创建了要运行的所有任务的列表:
List<Task> tasks = new List<Task>();
workers.ForEach(worker => tasks.Add(Task.Run(() => worker.Start())));
然后在await
try
块所包围的列表中使用catch
:
try
{
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
DebugLogger.Write("uh oh.. it died");
}
另外,请确保您在Thread.Wait(xxx)
内没有进行任何Thread.XXX
次调用(或任何其他ScheduleTimer()
调用),因为任务和线程不能很好地协同工作。
希望它有所帮助!