我正在使用允许控制台应用程序使用async / await关键字的Steven Toub's excellent AsyncPump class。
但是,我遇到一个问题,即代码中抛出的异常会被泵捕获然后重新抛出,这会导致原始调用堆栈和异常上下文丢失。
这是我的测试代码:
class Program
{
static void Main(string[] arg)
{
AsyncPump.Run(() => MainAsync());
}
static async Task MainAsync()
{
throw new Exception(); // code should break here
}
}
如果您运行此测试,调试器不会根据需要在throw new Exception()
上中断。相反,它会在t.GetAwaiter().GetResult()
上中断,这是AsyncPump类本身的一部分。这使调试应用程序非常困难。
有没有办法重新抛出异常,以便调试器在保留调用堆栈和上下文的同时在原始位置中断?
答案 0 :(得分:2)
GetAwaiter().GetResult()
已经正确地重新抛出异常(假设您使用的是.NET 4.5)。调用堆栈已正确保留。
您正在观察的是被捕获的顶级异常的行为,并且AFAIK严格将其视为VS同步,并且无法影响它。听起来它会很好UserVoice item。
答案 1 :(得分:2)
如果您使用async void
的{{1}}签名而非MainAsync
,则可能会看到所需的行为。这并不意味着您应该更改代码(async Task
几乎不是一个好主意),它只是意味着现有行为完全正常。
不会立即重新抛出async void
方法引发的异常。相反,它存储在async Task
对象内(具有捕获的堆栈上下文),并且当通过Task
,task.Result
,{task.Wait()
{{}} {{}时,将重新抛出任务的结果。 {1}}或await task
。
我发布了一些更详细的解释:TAP global exception handler。
在旁注中,我使用了task.GetAwaiter().GetResult()
的略微修改版本,这确保初始任务开始异步执行(即,在核心循环开始抽取之后),AsyncPump
为{ {1}}:
TaskScheduler.Current
也可以改变这一部分:
TaskScheduler.FromCurrentSynchronizationContext()
对此:
/// <summary>
/// PumpingSyncContext, based on AsyncPump
/// http://blogs.msdn.com/b/pfxteam/archive/2012/02/02/await-synchronizationcontext-and-console-apps-part-3.aspx
/// </summary>
class PumpingSyncContext : SynchronizationContext
{
BlockingCollection<Action> _actions;
int _pendingOps = 0;
public TResult Run<TResult>(Func<Task<TResult>> taskFunc, CancellationToken token = default(CancellationToken))
{
_actions = new BlockingCollection<Action>();
SynchronizationContext.SetSynchronizationContext(this);
try
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(
async () =>
{
OperationStarted();
try
{
return await taskFunc();
}
finally
{
OperationCompleted();
}
},
token, TaskCreationOptions.None, scheduler).Unwrap();
// pumping loop
foreach (var action in _actions.GetConsumingEnumerable())
action();
return task.GetAwaiter().GetResult();
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
}
void Complete()
{
_actions.CompleteAdding();
}
// SynchronizationContext methods
public override SynchronizationContext CreateCopy()
{
return this;
}
public override void OperationStarted()
{
// called when async void method is invoked
Interlocked.Increment(ref _pendingOps);
}
public override void OperationCompleted()
{
// called when async void method completes
if (Interlocked.Decrement(ref _pendingOps) == 0)
Complete();
}
public override void Post(SendOrPostCallback d, object state)
{
_actions.Add(() => d(state));
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotImplementedException("Send");
}
}
在这种情况下,异常将作为return task.GetAwaiter().GetResult();
传播给调用方,return task.Result;
指向AggregateException
方法内的原始异常。