日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

不要把异常当做业务逻辑,这性能可能你无法承受

發(fā)布時(shí)間:2023/12/4 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 不要把异常当做业务逻辑,这性能可能你无法承受 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一:背景

1. 講故事

在項(xiàng)目中摸爬滾打幾年,應(yīng)該或多或少的見過有人把異常當(dāng)做業(yè)務(wù)邏輯處理的情況(┬_┬),比如說判斷一個(gè)數(shù)字是否為整數(shù),就想當(dāng)然的用try catch包起來,再進(jìn)行?int.Parse,如果拋異常就說明不是整數(shù),簡單粗暴,也不需要寫正則或者其他邏輯,再比如一個(gè)字符串強(qiáng)制轉(zhuǎn)化為Enum,直接用Enum.Parse,可能是因?yàn)閷?duì)異常的開銷不是特別了解,這種不好的使用習(xí)慣也許被官方發(fā)現(xiàn)了,后續(xù)給我們補(bǔ)了很多的Try前綴的方法,比如:int.TryParse?,?Enum.TryParse,?dict.TryGetValue?,用代碼展示如下:

//原始寫法var num = int.Parse("1");//使用try方式var result = 0;var b = int.TryParse("1", out result);

用Try系列方法沒毛病,但這寫法讓人吐槽,還要單獨(dú)定義result變量,沒撤,官方還得靠我們這些開發(fā)者給他們發(fā)揚(yáng)光大????????????,終于在C# 7.0 中新增了一個(gè)?out variables?語法糖。

//try out 變量模式var c = int.TryParse("1", out int result2);

這種?out 變量?模式就????????了,一個(gè)方法獲取兩個(gè)值,還沒有拋異常的風(fēng)險(xiǎn)。

二:為什么要用tryxxx方法

有了tryxxx方法之后,你就應(yīng)該明白微軟已經(jīng)在提醒我們開發(fā)人員不要濫用異常,尤其在可預(yù)知可預(yù)見的場(chǎng)景下,畢竟他們知道異常的開銷真的是太大了,不知者不怪哈。

1. 肉眼看得見的低性能

為了讓大家肉眼能看見,我們就用異常方法和tryxxx方法做一個(gè)性能比較,迭代50w次,看看各自的性能如何?

for (int i = 0; i < 3; i++){var watch = Stopwatch.StartNew();for (int k = 0; k < 50000; k++){try{var num = int.Parse("xxx");}catch (Exception ex) { }}watch.Stop();Console.WriteLine($"i={i + 1},耗費(fèi):{watch.ElapsedMilliseconds}");}Console.WriteLine("---------------------------------------------");for (int i = 0; i < 3; i++){var watch = Stopwatch.StartNew();for (int k = 0; k < 50000; k++){var num = int.TryParse("xxx", out int reuslt);}watch.Stop();Console.WriteLine($"i={i + 1},耗費(fèi):{watch.ElapsedMilliseconds}");}Console.ReadLine();

看結(jié)果還挺嚇人的,相差480倍, 好熟悉的一個(gè)數(shù)字。。。?南朝四百八十寺,多少樓臺(tái)煙雨中?????????????

三:異常的超強(qiáng)開銷

為什么異常有那么大的開銷?只有知己知彼才能心中有數(shù),看過我多線程視頻的朋友應(yīng)該知道,線程的創(chuàng)建和銷毀代價(jià)都是非常大的,其中有一項(xiàng)就是需要代碼從用戶態(tài)切換到了內(nèi)核態(tài),畢竟線程是操作系統(tǒng)層面的事情,和你CLR無關(guān),CLR只是做了一層系統(tǒng)包裝而已,其實(shí)很多人都想不到,我們用的?try catch finally?底層也是封裝了操作系統(tǒng)層面的(Windows 結(jié)構(gòu)化異常處理),也叫做SEH,什么意思?就是當(dāng)你throw之后,代碼需要從用戶態(tài)切換到內(nèi)核態(tài),這個(gè)開銷是不會(huì)小的,還有一個(gè)開銷來自于Exception中的StackTrace,這里面的值需要從當(dāng)前異常的線程棧中去抓取調(diào)用堆棧,棧越深,開銷就越大。

1. 從用戶態(tài)到內(nèi)核態(tài)

大家肯定會(huì)說,甭那么玄乎,凡事都要講個(gè)證據(jù),?Do more,Talk less, 這里我準(zhǔn)備分兩種情況講解。

<1> 有catch情況

準(zhǔn)備在catch的時(shí)候阻塞住,然后抓它的dump文件。

public static void Main(string[] args){try{var num = int.Parse("xxx");}catch (Exception ex){Console.WriteLine(ex.Message);Console.ReadLine();}}

使用?!dumpstack?把當(dāng)前 0號(hào)線程 的所有托管和非托管堆棧全部打出來,簡化后如下:

0:000> ~0s ntdll!NtReadFile+0x14: 00007fff`f805aa64 c3 ret 0:000> !dumpstack OS Thread Id: 0x2bf0 (0) Current frame: ntdll!NtReadFile+0x14 Caller, Callee (MethodDesc 00007fffde3a40b8 +0x18 System.Console.ReadLine()) (MethodDesc 00007fff810d59f8 +0xa5 ConsoleApp4.Program.Main(System.String[])), calling (MethodDesc 00007fffde3a40b8 +0 System.Console.ReadLine()) 00000044433fc700 00007fffe07a29e0 clr!ExceptionTracker::CallCatchHandler+0x9c, calling clr!ExceptionTracker::CallHandler clr!ClrUnwindEx+0x40, calling ntdll!RtlUnwindEx ntdll!RtlRaiseException+0x4e, calling ntdll!RtlpCaptureContext clr!IL_Throw+0x114, calling clr!RaiseTheExceptionInternalOnly (MethodDesc 00007fffde4f95c0 System.Number.StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)), calling mscorlib_ni+0x53976a (MethodDesc 00007fffde3b5330 +0xae System.Number.ParseInt32(System.String, System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo)), calling (MethodDesc 00007fffde4f95c0 +0 System.Number.StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)) (MethodDesc 00007fffde1ebfa8 +0x2eb System.Globalization.NumberFormatInfo..ctor(System.Globalization.CultureData)), calling (MethodDesc 00007fffde1eba68 +0 System.Globalization.CultureData.GetNFIValues(System.Globalization.NumberFormatInfo)) (MethodDesc 00007fff810d59f8 +0x49 ConsoleApp4.Program.Main(System.String[])), calling (MethodDesc 00007fffde3b1708 +0 System.Int32.Parse(System.String))

因?yàn)槭嵌褩?#xff0c;所以執(zhí)行流就要從后往前看,你會(huì)發(fā)現(xiàn)流程大概是這個(gè)樣子?int.Parse -> CLR -> ntdll -> CLR -> Console.ReadLine?,很顯然 ntdll.dll 是操作系統(tǒng)層級(jí)的一個(gè)核心文件,這就從用戶態(tài)切入到了內(nèi)核態(tài),如果不是很明白,我畫一張簡圖吧。。。

<2>. 無catch處理

大家肯定很好奇,如果無catch會(huì)是怎么樣,大家也可以用windbg去挖一下。

public static void Main(string[] args){var num = int.Parse("xxx");}0:000> !dumpstack OS Thread Id: 0xd68 (0) Current frame: ntdll!NtTerminateProcess+0x14 Caller, Callee mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes+0x285, calling KERNEL32!ExitProcessImplementation mscoreei!CLRRuntimeHostInternalImpl::ShutdownAllRuntimesThenExit+0x14, calling mscoreei!RuntimeDesc::ShutdownAllActiveRuntimes clr!EEPolicy::ExitProcessViaShim+0x9c clr!SafeExitProcess+0x9d, calling clr!EEPolicy::ExitProcessViaShim ntdll!KiUserExceptionDispatch+0x53, calling ntdll!NtRaiseException clr!RaiseTheExceptionInternalOnly+0x188426, calling clr!EEPolicy::HandleFatalError clr!IL_Throw+0x45, calling clr!LazyMachStateCaptureState (MethodDesc 00007fffde4f95c0 System.Number.StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)), calling mscorlib_ni+0x53976a (MethodDesc 00007fffde3b5330 +0xae System.Number.ParseInt32(System.String, System.Globalization.NumberStyles, System.Globalization.NumberFormatInfo)), calling (MethodDesc 00007fffde4f95c0 +0 System.Number.StringToNumber(System.String, System.Globalization.NumberStyles, NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)) (MethodDesc 00007fffde1ebfa8 +0x2eb System.Globalization.NumberFormatInfo..ctor(System.Globalization.CultureData)), calling (MethodDesc 00007fffde1eba68 +0 System.Globalization.CultureData.GetNFIValues(System.Globalization.NumberFormatInfo)) (MethodDesc 00007fff810e59f8 +0x37 ConsoleApp4.Program.Main(System.String[])), calling (MethodDesc 00007fffde3b1708 +0 System.Int32.Parse(System.String))

可以看到進(jìn)程的退出邏輯給了托管程序入口?mscoreei.dll?而再也沒有進(jìn)入Main函數(shù)了, 為此我也補(bǔ)一張圖給大家看看

2. 抓取線程調(diào)用棧

當(dāng)大家慌慌張張的看到異常的時(shí)候,第一眼會(huì)去看異常信息是什么?第二眼會(huì)去看異常出在了哪一行代碼,這就是線程的調(diào)用棧,這個(gè)信息非常重要,可以快捷的幫助我們找到問題解決問題,放在Exception的StackTrace中,先上一段代碼。

public static void Main(string[] args){Run();Console.ReadLine();}public static void Run(){var ex = new FormatException("你的格式錯(cuò)誤啦!!!");throw ex;}

<1> StackTrace何時(shí)塞入的

到目前為止還沒看到哪本書說到StackTrace是何時(shí)被塞入的?由于水平有限,我也試著探測(cè)一下下。

從代碼中可以看到不是在new的時(shí)候塞入的,那會(huì)是哪里呢?

<2> 從CLR中尋找答案

既然不在用戶代碼,那就到CLR中去看看,在windbg中用?dumpstack?去查看非托管堆棧。

0:000> !dumpstack OS Thread Id: 0x4090 (0) Current frame: ntdll!NtTerminateProcess+0x14 Caller, Callee clr!EETypeHashTable::FindItem+0x532, calling clr!NgenHashTable<EEClassHashTable,EEClassHashEntry,4>::PersistedBucketList::GetBucket clr!JIT_StrCns+0xd0, calling clr!HelperMethodFrameRestoreState (MethodDesc 00007fff810f5a08 +0x70 ConsoleApp4.Program.Run()), calling clr!IL_Throw clr!IL_Throw+0x45, calling clr!LazyMachStateCaptureState (MethodDesc 00007fff810f5a08 +0x70 ConsoleApp4.Program.Run()), calling clr!IL_Throw (MethodDesc 00007fff810f59f8 +0x28 ConsoleApp4.Program.Main(System.String[])), calling 00007fff81200488 (stub for ConsoleApp4.Program.Run())

從簡化后的流程看,懷疑是由?clr!HelperMethodFrameRestoreState?處理的,為什么這么說呢?因?yàn)槲覀兌x的?FormatException ex?會(huì)傳給CLR的,不信可以用?kb?看一看。

0:000> kb# RetAddr : Args to Child : Call Site 00 00007fff`e07a3181 : 00000000`e0434352 0000006d`4a7fe938 0000017b`30ad2d48 0000017b`2f081690 : KERNELBASE!RaiseException+0x68 01 00007fff`e07a45f4 : ffffffff`fffffffe 0000017b`2ef02542 00000000`0000000a 0000017b`2f040910 : clr!RaiseTheExceptionInternalOnly+0x31f 02 00007fff`811d0950 : 00000000`70000001 00007fff`810c4140 0000006d`4a7fedb8 0000006d`4a7fec78 : clr!IL_Throw+0x114 03 00007fff`811d08b8 : 0000017b`30ad2d30 00007fff`810c4140 00000000`00000000 00007fff`00000000 : 0x00007fff`811d0950 04 00007fff`e0736c93 : 0000017b`30ad2d30 00007fff`810c4140 00000000`00000000 00007fff`00000000 : 0x00007fff`811d08b8 05 00007fff`e0736b79 : 00000000`00000000 00007fff`e0737aae 0000006d`4a7fefb8 00000000`00000000 : clr!CallDescrWorkerInternal+0x83 06 00007fff`e0737410 : 0000006d`4a7fefb8 0000006d`4a7ff048 0000006d`4a7feeb8 00000000`00000001 : clr!CallDescrWorkerWithHandler+0x4e 07 00007fff`e08dcaf2 : 0000006d`4a7fee00 00000000`00000001 00000000`00000001 0000017b`2efcecf0 : clr!MethodDescCallSite::CallTargetWorker+0x102 08 00007fff`e08dd4b3 : 00000000`00000001 00000000`00000000 0000017b`30ad2d30 0000017b`30ad2d30 : clr!RunMain+0x25f 09 00007fff`e08dd367 : 0000017b`2f040910 0000006d`4a7ff420 0000017b`2f040910 0000017b`2f082770 : clr!Assembly::ExecuteMainMethod+0xb7 0a 00007fff`e08dccb3 : 00000000`00000000 0000017b`2ef00000 00000000`00000000 00000000`00000000 : clr!SystemDomain::ExecuteMainMethod+0x643 0b 00007fff`e08dcc31 : 0000017b`2ef00000 00007fff`e08de090 00000000`00000000 00000000`00000000 : clr!ExecuteEXE+0x3f 0c 00007fff`e08de0a4 : ffffffff`ffffffff 00007fff`e08de090 00000000`00000000 00000000`00000000 : clr!_CorExeMainInternal+0xb2 0d 00007fff`e1208a61 : 00000000`00000000 00007fff`00000091 00000000`00000000 0000006d`4a7ff9f8 : clr!CorExeMain+0x14 0e 00007fff`e133a4cc : 00000000`00000000 00007fff`e08de090 00000000`00000000 00000000`00000000 : mscoreei!CorExeMain+0x112 0f 00007fff`f5cc4034 : 00007fff`e1200000 00000000`00000000 00000000`00000000 00000000`00000000 : MSCOREE!CorExeMain_Exported+0x6c 10 00007fff`f8033691 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14 11 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

其中第一行的?00 00007fffe07a3181 : 00000000e0434352 0000006d4a7fe938 0000017b30ad2d48 0000017b2f081690 : KERNELBASE!RaiseException+0x68中的第三個(gè)參數(shù)地址0000017b30ad2d48` 就是我們的異常類,打印出來看一下。

0:000> !do 0000017b30ad2d48 Name: System.FormatException MethodTable: 00007fffde285c38 EEClass: 00007fffde3930e0 Size: 160(0xa0) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields:MT Field Offset Type VT Attr Value Name 00007fffde2059c0 40002a2 8 System.String 0 instance 0000017b30ad4c80 _className 00007fffde282a50 40002a3 10 ...ection.MethodBase 0 instance 0000000000000000 _exceptionMethod 00007fffde2059c0 40002a4 18 System.String 0 instance 0000000000000000 _exceptionMethodString 00007fffde2059c0 40002a5 20 System.String 0 instance 0000017b30ad2de8 _message 00007fffde2883d8 40002a6 28 ...tions.IDictionary 0 instance 0000000000000000 _data 00007fffde205b70 40002a7 30 System.Exception 0 instance 0000000000000000 _innerException 00007fffde2059c0 40002a8 38 System.String 0 instance 0000000000000000 _helpURL 00007fffde205dd8 40002a9 40 System.Object 0 instance 0000017b30ad2e98 _stackTrace 00007fffde205dd8 40002aa 48 System.Object 0 instance 0000017b30ad2f28 _watsonBuckets 00007fffde2059c0 40002ab 50 System.String 0 instance 0000000000000000 _stackTraceString 00007fffde2059c0 40002ac 58 System.String 0 instance 0000000000000000 _remoteStackTraceString 00007fffde2085a0 40002ad 88 System.Int32 1 instance 0 _remoteStackIndex 00007fffde205dd8 40002ae 60 System.Object 0 instance 0000000000000000 _dynamicMethods 00007fffde2085a0 40002af 8c System.Int32 1 instance -2146233033 _HResult 00007fffde2059c0 40002b0 68 System.String 0 instance 0000000000000000 _source 00007fffde2831f8 40002b1 78 System.IntPtr 1 instance 0 _xptrs 00007fffde2085a0 40002b2 90 System.Int32 1 instance -532462766 _xcode 00007fffde21e720 40002b3 80 System.UIntPtr 1 instance 0 _ipForWatsonBuckets 00007fffde1f5080 40002b4 70 ...ializationManager 0 instance 0000017b30ad2e18 _safeSerializationManager 00007fffde205dd8 40002a1 100 System.Object 0 shared static s_EDILock>> Domain:Value 0000017b2efe0af0:NotInit <<0:000> !do 0000017b30ad2e98 Name: System.SByte[] MethodTable: 00007fffde20dde8 EEClass: 00007fffde390920 Size: 120(0x78) bytes Array: Rank 1, Number of elements 96, Type SByte (Print Array)Content: .........../{...P.......@..Jm....Z.........................Jm....Y.............................. Fields: None

此時(shí)?_stackTrace?已經(jīng)有值了,畢竟Console上已經(jīng)打印出來了。

最后補(bǔ)充一下大家也可以通過?!threads?去找異常的線程,如下圖的中?System.FormatException 0000017b30ad2d48,然后通過?!printexception?去打印這個(gè)地址?0000017b30ad2d48?上異常對(duì)象。

0:000> !threads ThreadCount: 2 UnstartedThread: 0 BackgroundThread: 1 PendingThread: 0 DeadThread: 0 Hosted Runtime: noLock ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception0 1 80c 0000016816f508f0 2a020 Preemptive 0000016818CCE3B8:0000016818CCFFD0 0000016816ef0b10 0 MTA System.FormatException 0000017b30ad2d486 2 12d8 0000016816f7b0e0 2b220 Preemptive 0000000000000000:0000000000000000 0000016816ef0b10 0 MTA (Finalizer)0:000> !printexception 0000017b30ad2d48 Exception object: 0000017b30ad2d48 Exception type: System.FormatException Message: 你的格式錯(cuò)誤啦!!! InnerException: <none> StackTrace (generated):SP IP Function0000001F8F7FEE90 00007FFF811E0951 ConsoleApp4!ConsoleApp4.Program.Run()+0x710000001F8F7FEEE0 00007FFF811E08B9 ConsoleApp4!ConsoleApp4.Program.Main(System.String[])+0x29StackTraceString: <none> HResult: 80131537

三:總結(jié)

不要把異常當(dāng)做業(yè)務(wù)邏輯處理,這開銷有可能你承受不起,把那些真正不可期的情況留給異常吧,如: TimeoutException。。。

總結(jié)

以上是生活随笔為你收集整理的不要把异常当做业务逻辑,这性能可能你无法承受的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 狠狠干综合网 | 伊人av影院 | www欧美视频 | 中文在线观看免费网站 | 国产美女福利在线 | 春色激情 | 日韩欧美国产中文字幕 | 国产黄色美女视频 | 综合国产一区 | 成年人天堂| 久久亚洲免费 | 极品熟妇大蝴蝶20p 国产偷自拍视频 | 欧美性精品 | 日本韩国在线播放 | 河北彩花av在线播放 | 中文字字幕码一二三区 | www视频在线| 亚洲国产天堂av | 色偷偷网 | 人妻丰满熟妇av无码区不卡 | 亚洲视频播放 | 欧美11一13sex性hd | 亚洲乱码国产乱码精品精剪 | 一级全黄毛片 | 亚洲一区二区三区在线免费观看 | 精品国产xxx | av激情影院 | 精品欧美一区二区精品久久 | 极品销魂美女少妇尤物 | 欧美午夜精品久久久久久人妖 | 成人黄色在线看 | 亚洲爽爆av| 暧暧视频在线观看 | 美国黄色av | 国产精品欧美一区喷水 | 一级特黄aa大片欧美 | 亚洲成人久久久久 | 一区二区三区视频网 | 国产视频欧美视频 | 日韩一区二区高清视频 | 日韩激情在线 | 中文字幕大全 | 久久国产视频一区 | 国产性70yerg老太 | www.久久婷婷| 午夜日韩福利 | av网址在线 | 黑人一区二区三区四区五区 | 91操视频 | 免费在线视频一区 | 中文字幕在线观看一区二区三区 | 久久综合色88 | 欧美激情一区二区三区在线 | 91麻豆精品国产91久久久无需广告 | 在线免费 | 日本高清视频在线观看 | 亚洲精品1区 | 一级视频在线观看 | 日本 在线 | 日本欧美亚洲 | 天堂男人在线 | 亚洲成人天堂 | 综综综综合网 | 欧美亚洲视频 | 亚洲精品一区中文字幕乱码 | 国产丰满果冻videossex | 久久成人资源 | 中文字幕一区电影 | 美女上床网站 | 星空大象mv高清在线观看免费 | 99热超碰在线 | 嫩草影院永久入口 | 青青草小视频 | 精品人妻无码一区二区三 | 看黄色一级片 | 久久午夜网 | 草久在线 | 性生活视频在线播放 | 亚洲午夜18毛片在线看 | 第一章豪妇荡乳黄淑珍 | 双乳被四个男人吃奶h文 | 中文毛片| 夜夜摸夜夜操 | 成年人免费黄色片 | 综合五月婷婷 | 久久久久久在线观看 | 亚洲精品乱码久久久久久麻豆不卡 | 超碰天天| 琪琪电影午夜理论片八戒八戒 | 亚欧美在线观看 | 欧美绿帽合集videosex | 一级全黄少妇性色生活片 | 久操久热 | 97人人人 | 国产精品日韩欧美一区二区三区 | 亚洲午夜精品久久久 | 亚洲精品三区 | 久久人人爽爽人人爽人人片av | 一区二区日韩电影 |