1.为ä»ä¹ä¸å»ºè®®ç¨ try catch
2.七爪源码:像专业人士一样在 Node.js 中处理错误
3.Golang源码剖析panic与recover,看不懂你打我好了
4.C++å¦ä½ä½¿ç¨try-catch
为ä»ä¹ä¸å»ºè®®ç¨ try catch
ä¸åè¯è¨å¤çTry Catchçæºå¶ä¸ä¸æ ·ï¼æ以ä¹ä¼æä¸ååçã
æ¯å¦C++ï¼æ¯ä¸æ¨èç¨try catchçï¼å®æ¨è使ç¨Windows APIé£ç§HResultæ¥è¿åé误æ åµï¼åå æ¯try catchä¼å¨å·²æç代ç ä¸é¢å¢å é¢å¤çcost, è¿ä¸ªé¢å¤çcostä¸æ¯è¯´åªæthrow exceptionçæ¶åæä¼æï¼èæ¯å¨try catch blockéé¢çæ¯ä¸è¡ä»£ç ä¸é½ä¼æï¼è¿ä¹æ¯ä¸ºä»ä¹ä»ä¸å»ºè®®ä½ 使ç¨try catchæ主è¦çåå ãå¨Windowsçæºä»£ç ä¸ï¼æ¯æ²¡æä»»ä½try catchçï¼å ¨é¨ç¨HResultæ¥å¤çã
æ¯å¦C#, try catchæ¯å»ºè®®ä½¿ç¨çï¼C#设计çæ¶åå¸åçC++ try catchçæè®ï¼æ以ç´æ¥ç¨Try catchå 裹已æ代ç å¢å çcostå¯ä»¥å¿½ç¥ä¸è®¡ï¼ä½æ¯å¦æççå¨ä»£ç è¿è¡è¿ç¨ä¸throw exceptionäºï¼è¿ä¸ªcostè¿æ¯å¾å¤§çãæ以ï¼å¨C#代ç 设计ä¸ï¼throw exceptionåºæ¬ä¸æ¯ä½ 认为ä¸ä¼åçè¿ç§æå¤çæ åµä¸ï¼å¦åï¼å¦ææ¯å¸¸è§é误ï¼æ好ä¸è¦throw exceptionã
æ¯å¦Java, try catchä¹æ¯å»ºè®®ä½¿ç¨çï¼æè¿ä¸ªç¨çä¸çï¼ä¸è¿çå®ç说æï¼å³ä½¿æ¯throw exceptionçæ¶åçcostä¹å¾å°ã
æ»ç»è¯´æ¥ï¼try catchæ¯å¦å»ºè®®ä½¿ç¨è¦çå ·ä½è¯è¨ï¼æéè¦è¡¡éçæ åå°±æ¯å®å¯¹å·²æç代ç æ§è½æå¤å¤§çå½±åãä½æ¯ä»å®è®¾è®¡çè§åº¦å°±æ¯ä¸ºäºå¤çä¸äºææä¸å°çæ åµï¼ä½æ¯å 为å½åå¼å ¥çæ¶ååç§åæ ·çåå ï¼å¯¼è´æäºè¯è¨ä¸ºäºæ§è½ï¼ä¸æ¨è使ç¨ã
BTW, try catchæ好ä¸è¦catch (Exception), è¿æ ·ä¼åæä¸è¯¥åçé®é¢ï¼æ¯å¦C#ä¸çStackOverflowException, OutOfMemoryException, NullReferenceException etc. 该crashçæ¶åå°±åºè¯¥è®©App crash, restart, è¿ä¹æ¯ä¿æ¤ä½ serviceçä¸ä¸ªå¥½æ¹æ³ã
å¸æå¯¹ä½ æ帮å©~
七爪源码:像专业人士一样在 Node.js 中处理错误
处理错误是构建生产级应用的关键。本文将指导你如何像专业人士一样在 Node.js 中处理错误。
错误并非都相同。了解不同类型的错误有助于你对它们进行分类和处理。首先,60级最好的源码了解所有可能的错误情况,然后学会轻松应对它们。
处理未找到的 URL 错误。当遇到用户试图访问不存在的 URL,如 /user 而非 /users,需要通知用户。在 ExpressJS 中,只需在所有路由之后添加特殊中间件,即可捕获所有未匹配的路由并返回正确的错误响应。
使用特殊中间件处理所有错误。在 Express 中,有一个特定的中间件负责处理所有错误,确保它放置在所有其他中间件和路由定义之后。将此中间件添加到索引文件,确保所有错误均被处理。
自定义错误对象。默认的错误对象在抛出错误时提供有限信息。可以创建一个自定义错误类,添加更多属性,源码阁UI源码并区分各种错误对象。这有助于提高错误处理的精确度。
在路由内部处理错误。抛出自定义错误,实现错误处理的灵活性和控制性。利用错误对象调用下一个函数,提高代码的可维护性和整洁性。
创建自定义包装函数。捕获所有错误,并从中心位置调用下一个函数,消除 try/catch 块的使用,简化代码结构。
微调错误处理。根据需求定制错误类,例如创建一个处理未找到的路由的新错误类,简化错误处理流程,添加有意义的状态码,让代码更具可读性和功能。
优雅处理程序员错误。遇到未处理的承诺拒绝等错误时,优雅地重启应用,以避免出现不可预料的问题。使用错误中间件集中处理错误,遵循单一责任原则,jdk 源码 jvm源码分离关注点。
最后,通过实践,你将能更熟练地处理 Node.js 中的错误,构建更稳定、更安全的应用程序。不断学习和实践,你的编程技能将得到显著提升。愿你在编程之旅中取得更多成就。
Golang源码剖析panic与recover,看不懂你打我好了
哈喽,大家好,我是asong,今天与大家来聊一聊go语言中的"throw、try.....catch{ }"。如果你之前是一名java程序员,我相信你一定吐槽过go语言错误处理方式,但是这篇文章不是来讨论好坏的,我们本文的重点是带着大家看一看panic与recover是如何实现的。上一文我们讲解了defer是如何实现的,但是没有讲解与defer紧密相连的recover,想搞懂panic与recover的实现也没那么简单,就放到这一篇来讲解了。废话不多说,IM源码IM源码直接开整。
Go 语言中panic 关键字主要用于主动抛出异常,类似 java 等语言中的 throw 关键字。panic 能够改变程序的控制流,调用 panic 后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的 defer;
Go 语言中recover 关键字主要用于捕获异常,让程序回到正常状态,类似 java 等语言中的 try ... catch 。recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;
recover只能在defer中使用这个在标准库的注释中已经写明白了,我们可以看一下:
这里有一个要注意的点就是recover必须要要在defer函数中使用,否则无法阻止panic。最好的验证方法是先写两个例子:
运行我们会发现example2()方法的panic是没有被recover住的,导致整个程序直接crash了。这里大家肯定会有疑问,为什么直接写recover()就不能阻止panic了呢。我们在 详解defer实现机制(附上三道面试题,我不信你们都能做对)讲解了defer实现原理,一个重要的知识点**defer将语句放入到栈中时,也会将相关的值拷贝同时入栈。**所以defer recover()这种写法在放入defer栈中时就已经被执行过了,panic是发生在之后,所以根本无法阻止住panic。源码精灵传说源码蛋
通过运行结果可以看出panic不会影响defer函数的使用,所以他是安全的。
这里我开了两个协程,一个协程会发生panic,导致程序崩溃,但是只会执行自己所在Goroutine的延迟函数,所以正好验证了多个 Goroutine 之间没有太多的关联,一个 Goroutine 在 panic 时也不应该执行其他 Goroutine 的延迟函数。
其实我们在实际项目开发中,经常会遇到panic问题, Go 的 runtime 代码中很多地方都调用了 panic 函数,对于不了解 Go 底层实现的新人来说,这无疑是挖了一堆深坑。我们在实际生产环境中总会出现panic,但是我们的程序仍能正常运行,这是因为我们的框架已经做了recover,他已经为我们兜住底,比如gin,我们看一看他是怎么做的。
我们先来写个简单的代码,看看他的汇编调用:执行go tool compile -N -l -S main.go就可以看到对应的汇编码了,我们截取部分片段分析:
上面重点部分就是画红线的三处,第一步调用runtime.deferprocStack创建defer对象,这一步大家可能会有疑惑,我上一文忘记讲个这个了,这里先简单概括一下,defer总共有三种模型,编译一个函数里只会有一种defer模式。在讲defer实现机制时,我们一起看过defer的结构,其中有一个字段就是_panic,是触发defer的作用,我们来看看的panic的结构:
简单介绍一下上面的字段:
上面的pc、sp、goexit我们单独讲一下,runtime包中有一个Goexit方法,Goext能够终止调用它的goroutine,其他的goroutine是不受影响的,goexit也会在终止goroutine之前运行所有延迟调用函数,Goexit不是一个panic,所以这些延迟函数中的任何recover调用都将返回nil。如果我们在主函数中调用了Goexit会终止该goroutine但不会返回func main。由于func main没有返回,因此程序将继续执行其他gorountine,直到所有其他goroutine退出,程序才会crash。
下面就开始我们的重点吧~。
在讲defer实现机制时,我们一起看过defer的结构,其中有一个字段就是_panic,是触发defer的作用,我们来看看的panic的结构:简单介绍一下上面的字段:上面的pc、sp、goexit我们单独讲一下,runtime包中有一个Goexit方法,Goext能够终止调用它的goroutine,其他的goroutine是不受影响的,goexit也会在终止goroutine之前运行所有延迟调用函数,Goexit不是一个panic,所以这些延迟函数中的任何recover调用都将返回nil。如果我们在主函数中调用了Goexit会终止该goroutine但不会返回func main。由于func main没有返回,因此程序将继续执行其他gorountine,直到所有其他goroutine退出,程序才会crash。写个简单的例子:运行上面的例子你就会发现,即使在主goroutine中调用了runtime.Goexit,其他goroutine是没有任何影响的。所以结构中的pc、sp、goexit三个字段都是为了修复runtime.Goexit,这三个字段就是为了保证该函数的一定会生效,因为如果在defer中发生panic,那么goexit函数就会被取消,所以才有了这三个字段做保护。看这个例子:
英语好的可以看一看这个: github.com/golang/go/is...,这就是上面的一个例子,这里就不过多解释了,了解就好。
接下来我们再来看一看gopanic方法。
gopanic的代码有点长,我们一点一点来分析:
根据不同的类型判断当前发生panic错误,这里没什么多说的,接着往下看。
上面的代码都是截段,这些部分都是为了判断当前defer是否可以使用开发编码模式,具体怎么操作的就不展开了。
在第三部分进行defer内联优化选择时会执行调用延迟函数(reflectcall就是这个作用),也就是会调用runtime.gorecover把recoverd = true,具体这个函数的操作留在下面讲,因为runtime.gorecover函数并不包含恢复程序的逻辑,程序的恢复是在gopanic中执行的。先看一下代码:
这段代码有点长,主要就是分为两部分:
第一部分主要是这个判断if gp._panic != nil && gp._panic.goexit && gp._panic.aborted { ... },正常recover是会绕过Goexit的,所以为了解决这个,添加了这个判断,这样就可以保证Goexit也会被recover住,这里是通过从runtime._panic中取出了程序计数器pc和栈指针sp并且调用runtime.recovery函数触发goroutine的调度,调度之前会准备好 sp、pc 以及函数的返回值。
第二部分主要是做panic的recover,这也与上面的流程基本差不多,他是从runtime._defer中取出了程序计数器pc和栈指针sp并调用recovery函数触发Goroutine,跳转到recovery函数是通过runtime.call进行的,我们看一下其源码(src/runtime/asm_amd.s 行):
因为go语言中的runtime环境是有自己的堆栈和goroutine,recovery函数也是在runtime环境执行的,所以要调度到m->g0来执行recovery函数,我们在看一下recovery函数:
在recovery 函数中,利用 g 中的两个状态码回溯栈指针 sp 并恢复程序计数器 pc 到调度器中,并调用 gogo 重新调度 g , goroutine 继续执行,recovery在调度过程中会将函数的返回值设置为1。这个有什么作用呢? 在deferproc函数中找到了答案:
当延迟函数中recover了一个panic时,就会返回1,当 runtime.deferproc 函数的返回值是 1 时,编译器生成的代码会直接跳转到调用方函数返回之前并执行 runtime.deferreturn,跳转到runtime.deferturn函数之后,程序就已经从panic恢复了正常的逻辑。
在这里runtime.fatalpanic实现了无法被恢复的程序崩溃,它在中止程序之前会通过 runtime.printpanics 打印出全部的 panic 消息以及调用时传入的参数。
这就是这个逻辑流程,累死我了。。。。
结尾给大家发一个小福利,哈哈,这个福利就是如果避免出现panic,要注意这些:这几个是比较典型的,还有很多会发生panic的地方,交给你们自行学习吧~。
好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者持续创作更多优质内容的动力!
C++å¦ä½ä½¿ç¨try-catch
/// èµæºæ° ããpublic static int resourceNumber; ãã/// è¿ç¨æ° ããpublic static int processNumber; ãã/// å¯ç¨èµæºæ°ç» ããpublic static int[] Available; ãã/// å·¥ä½åé ããpublic static int[] work; ãã/// å®è¡¨ç¤ºç³»ç»æ¯å¦æ足å¤çèµæºåé ç»è¿ç¨ ããpublic static bool[] Finish; ãã/// æ大éæ±ç©éµ ããpublic static int[][] Max; ãã/// åé ç©éµ ããpublic static int[][] Allocation; ãã/// éæ±ç©éµ ããpublic static int[][] Need; ãã/// å®å ¨åºå ããpublic static int[] SafeSequence; ãã/// èµæºè¯·æ±åé ããpublic static int[] Request; ããç®æ³ææ³ï¼ ãã主è¦æ¯ï¼éå½+深度ä¼å æ寻+å溯 ããç®æ³æºä»£ç å¦ä¸ï¼ ãã/// æ·±æ+å溯å®ç°é¶è¡å®¶ç®æ³ ãã/// <param name="n">å·²å®æçè¿ç¨æ°</param> ããpublic void DFS_searchSafeSequence(int n) ãã{ ããif (n == processNumber) ãã{ ãã//æ¾å°ä¸ä¸ªå®å ¨åºå,å¯ä»¥æ¾ç¤ºææå®å ¨åºå ãã//æ¾ç¤ºå¨richTextBoxshow.Textä¸ ããfor (int i = 0; i < processNumber; i++) ãã{ ããrichTextBoxshow.Text += SafeSequence[i] + "ã"; ãã} ããrichTextBoxshow.Text += "\n"; ããreturn; ãã} ããfor (int i = 0; i < processNumber; i++) ãã{ ããif (Finish[i] == false)//è¿ç¨å°æªå®æ ãã{ ãã//å¤æç°æèµæºæ¯å¦å¯ä»¥æ»¡è¶³è¿ä¸ªè¿ç¨ ããbool isOK = true; ããfor (int j = 0; j < resourceNumber; j++) ãã{ ããif (Need[i][j] > work[j]) ãã{ ããisOK = false; ããbreak; ãã} ãã} ãã//å¯ä»¥æ»¡è¶³ ããif (isOK) ãã{ ãã//å è¯æ¢çå°èµæºåé ç»è¿ä¸ªè¿ç¨ ããfor (int j = 0; j < resourceNumber; j++) ãã{ ããwork[j] += Allocation[i][j]; ãã} ãã//å·²ç»å®æ ããFinish[i] = true; ãã//å å ¥å®å ¨åºå ããSafeSequence[n] = i; ãã//继ç»æç´¢ ããDFS_searchSafeSequence(n+1); ãã//å溯 ããFinish[i] = false; ããSafeSequence[n] = -1; ããfor (int j = 0; j < resourceNumber; j++) ãã{ ããwork[j] -= Allocation[i][j]; ãã} ãã} ãã} ãã} ãã}