問題
在使用 abp 框架的后臺作業時,當后臺作業拋出異常,會導致整個程序崩潰。在 abp 框架的底層執行后臺作業的時候,有 try/catch 語句塊用來捕獲后臺任務執行時的異常,但是在這里沒有生效。
原始代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class testappservice : itestappservice { private readonly ibackgroundjobmanager _backgroundjobmanager; public testappservice(ibackgroundjobmanager backgroundjobmanager) { _backgroundjobmanager = backgroundjobmanager; } public task getinvalidoperationexception() { throw new invalidoperationexception( "模擬無效操作異常。" ); } public async task<string> enqueuejob() { await _backgroundjobmanager.enqueueasync<bg, string>( "測試文本。" ); return "執行完成。" ; } } public class bg : backgroundjob<string>, itransientdependency { private readonly testappservice _testappservice; public bg(testappservice testappservice) { _testappservice = testappservice; } public override async void execute(string args) { await _testappservice.getinvalidoperationexception(); } } |
調用接口時的效果:
原因
出現這種情況是因為任何異步方法返回 void 時,拋出的異常都會在 async void 方法啟動時,處于激活狀態的同步上下文 (synchronizationcontext)
觸發,我們的所有 task 都是放在線程池執行的。
所以在上述樣例當中,此時 asyncvoidmethodbuilder.create()
使用的同步上下文為 null ,這個時候 threadpool 就不會捕獲異常給原有線程處理,而是直接拋出。
線程池在底層使用 asyncvoidmethodbuilder.craete()
所拿到的同步上下文,所捕獲異常的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
internal static void throwasync(exception exception, synchronizationcontext targetcontext) { var edi = exceptiondispatchinfo.capture(exception); // 同步上下文是空的,則不會做處理。 if (targetcontext != null ) { try { targetcontext.post(state => ((exceptiondispatchinfo)state). throw (), edi); return ; } catch (exception postexception) { edi = exceptiondispatchinfo.capture( new aggregateexception(exception, postexception)); } } } |
雖然你可以通過掛載 appdoamin.current.unhandledexception
來監聽異常,不過你是沒辦法從異常狀態恢復的。
解決
可以使用 asyncbackgroundjob<targs>
替換掉之前的 backgroundjob<targs>
,只需要實現它的 task executeasync(targs args)
方法即可。
1
2
3
4
5
6
7
8
9
10
11
12
|
public class bgasync : asyncbackgroundjob<string>,itransientdependency { private readonly testappservice _testappservice; public bgasync(testappservice testappservice) { _testappservice = testappservice; } protected override async task executeasync(string args) { await _testappservice.getinvalidoperationexception(); } } |
總結
以上所述是小編給大家介紹的為什么不要使用 async void的原因分析,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!