C#多線程中的異常處理操作示例
本文實(shí)例講述了C#多線程中的異常處理操作。分享給大家供大家參考,具體如下:
常規(guī)Thread中處理異常
使用Thread創(chuàng)建的子線程,需要在委托中捕捉,無法在上下文線程中捕捉
static void Main(string[] args)
{
ThreadStart threadStart = DoWork;
Thread thread = new Thread(threadStart);
thread.Start();
thread.Join();
}
static void DoWork()
{
try
{
throw new Exception("子線程出現(xiàn)異常了");
}
catch (Exception ex)
{
Trace.Assert(false, "Catch In Delegate");
}
}
Task中處理異常
1.仍然可以在委托中捕獲異常
2.可以捕獲Task.Wait() 或者 Task.Result 的 AggregateException 異常
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine($"Error: {ex.GetType().Name}");
foreach (Exception item in ex.InnerExceptions)
{
Console.WriteLine($"{item.GetType().Name}, {item.Message}");
}
}
AggregateException 是并行任務(wù)中捕獲的一組異常
通過延續(xù)任務(wù)捕獲前驅(qū)任務(wù)中的異常
static void Main(string[] args)
{
Task task = Task.Run(() => throw new Exception("前驅(qū)任務(wù)異常了"));
Task faultedTask = task.ContinueWith(antecedentTask =>
{
antecedentTask.Exception.Handle(eachE =>
{
Console.WriteLine($"Error: {eachE.Message}");
return true;
});
},TaskContinuationOptions.OnlyOnFaulted);
faultedTask.Wait();
}
前驅(qū)任務(wù):使用Run書寫的第一個(gè)任務(wù)就是前驅(qū)任務(wù)
延續(xù)任務(wù):在一個(gè)任務(wù)后使用ContinueWith添加的任務(wù)就是延續(xù)任務(wù),延續(xù)一般是一個(gè)全新的工作線程
TaskContinuationOptions:指定延續(xù)任務(wù)時(shí)的可配置項(xiàng),默認(rèn)情況下前驅(qū)任務(wù)完成后,立即執(zhí)行延續(xù)任務(wù),OnlyOnFaulted表示只有前驅(qū)任務(wù)失敗(出異常的時(shí)候)才會執(zhí)行這一個(gè)延續(xù)任務(wù)
Task.Exception也是一個(gè)AggregateException 異常
注意:
1.當(dāng)指定的TaskContinuationOptions與前驅(qū)任務(wù)運(yùn)行結(jié)果不一致時(shí),強(qiáng)制調(diào)用延續(xù)任務(wù)Wait()會引發(fā)TaskCanceledException異常
static void Main(string[] args)
{
Task task = new Task(() =>
{
Console.WriteLine("前驅(qū)動任務(wù)執(zhí)行中...");
});
Task faultedTask = task.ContinueWith(antecedentTask =>
{
Console.WriteLine("延續(xù)動任務(wù)執(zhí)行中...");
}, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
try
{
faultedTask.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine($"Error: {ex.GetType().Name}");
foreach (Exception item in ex.InnerExceptions)
{
Console.WriteLine($"{item.GetType().Name}, {item.Message}");
}
}
Console.WriteLine($"前驅(qū)任務(wù)狀態(tài){task.Status}");
Console.WriteLine($"延續(xù)任務(wù)狀態(tài){faultedTask.Status}");
}
Ctrl+F5 輸出

補(bǔ)充:
假如在前驅(qū)任務(wù)中出現(xiàn)了異常,如OnlyOnFaulted所愿,會執(zhí)行faultedTask任務(wù),并且在faultedTask.Wait()中不會捕捉到前驅(qū)任務(wù)的異常,具體看下面一點(diǎn)
2.延續(xù)任務(wù)雖然在異步任務(wù)中提供了類似if else 的ContinueWith但是在異常處理上還是有點(diǎn)局限,看一個(gè)例子
static void Main(string[] args)
{
Task task = Task.Run(()
=>
throw new Exception("前驅(qū)任務(wù)異常了"));
Task task1 = task.ContinueWith(antecedentTask =>
{
throw new Exception("延續(xù)任務(wù)1異常了");
});
Task task2 = task1.ContinueWith(antecedentTask =>
{
throw new Exception("延續(xù)任務(wù)2異常了");
});
Task task3 = task2.ContinueWith(antecedentTask =>
{
throw new Exception("延續(xù)任務(wù)3異常了");
});
try
{
task3.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine($"Error: {ex.GetType().Name}");
foreach (Exception item in ex.InnerExceptions)
{
Console.WriteLine($"{item.GetType().Name}, {item.Message}");
}
}
}
Ctrl+F5 輸出

其實(shí)這樣也可以理解,task3.Wait()只會收集task3所在工作線程上的異常,遺憾的是Task.Exception.InnerExceptions是一個(gè)只讀集合,這樣一來,每個(gè)任務(wù)的異常只能在各自委托中處理了,事實(shí)上也應(yīng)該如此,可以使用TaskContinuationOptions進(jìn)行靈活控制
使用CancellationTokenSource取消任務(wù)
static void Main(string[] args)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Token.Register(() =>
{
Console.WriteLine("任務(wù)取消了");
});
cancellationTokenSource.CancelAfter(2000);
Task task = Task.Run(() =>
{
while (true && !cancellationTokenSource.IsCancellationRequested)
{
Console.WriteLine("任務(wù)執(zhí)行中...");
Thread.Sleep(300);
}
},
cancellationTokenSource.Token);
task.Wait();
Console.WriteLine($"任務(wù)的最終狀態(tài)是:{task.Status}");
}
Ctrl+F5 輸出

正常取消的任務(wù)最終狀態(tài)是 RanToCompletion ,這里要注意的是,CancelAfter()是在這個(gè)方法調(diào)用的那一刻開始計(jì)時(shí)的(并非以Run開始計(jì)時(shí),好吧,很好理解,我卻疑惑了半天)
小結(jié):
結(jié)合 TaskContinuationOptions 和 CancellationTokenSource 可以很好處理多任務(wù)中異常,但是編寫在異步程序還是很繁瑣的,具體的在下一個(gè)筆記中會結(jié)合C#5.0做一個(gè)比較
更多關(guān)于C#相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《C#常見控件用法教程》、《WinForm控件用法總結(jié)》、《C#數(shù)據(jù)結(jié)構(gòu)與算法教程》、《C#面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》及《C#程序設(shè)計(jì)之線程使用技巧總結(jié)》
希望本文所述對大家C#程序設(shè)計(jì)有所幫助。
相關(guān)文章
c#多線程網(wǎng)絡(luò)聊天程序代碼分享(服務(wù)器端和客戶端)
本程序使用VS2005 制作,程序分為三塊,XuLIeHua類庫下有我寫的把結(jié)構(gòu)序列化的類,還有就是服務(wù)器端和客戶端2013-12-12
C#之HttpClient設(shè)置cookies的兩種方式
這篇文章主要介紹了C#之HttpClient設(shè)置cookies的兩種方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
js驗(yàn)證電話號碼手機(jī)號碼的正則表達(dá)式
本篇文章主要是對js驗(yàn)證電話號碼手機(jī)號碼的正則表達(dá)式進(jìn)行了介紹。需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01
C#8.0 中開啟默認(rèn)接口實(shí)現(xiàn)方法
這篇文章主要介紹了C#8.0 中開啟默認(rèn)接口實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧的相關(guān)資料2019-05-05
C#實(shí)現(xiàn)驗(yàn)證身份證是否合法的方法
這篇文章主要介紹了C#實(shí)現(xiàn)驗(yàn)證身份證是否合法的方法,實(shí)例分析了通過自定義函數(shù)實(shí)現(xiàn)針對身份證合法性驗(yàn)證的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03
C#使用String和StringBuilder運(yùn)行速度測試及各自常用方法簡介
今天小編就為大家分享一篇關(guān)于C#使用String和StringBuilder運(yùn)行速度測試及各自常用方法簡介,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10

