|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-cn" dir="ltr">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<meta name="description" content="恐慌与恢复 - panic/recover # 我们知道Go语言中许多错误会在编译时暴露出来,直接编译不通过,但对于空指针访问元素,切片/数组越界访问之类的运行时错误,只会在运行时引发 panic 异常暴露出来。这种由Go语言自动的触发的 panic 异常属于运行时panic(Run-time panics)1。当发生 panic 时候,Go会运行所有已经注册的延迟函数,若延迟函数中未进行panic异常捕获处理,那么最终Go进程会终止,并打印堆栈信息。此外Go中还内置了 panic 函数,可以用于用户手动触发panic。
|
|
|
Go语言中内置的 recover 函数可以用来捕获 panic异常,但 recover 函数只能放在延迟函数调用中,才能起作用。我们从之前的章节《 基础篇-语言特性-defer函数 》了解到,多个延迟函数,会组成一个链表。Go在发生panic过程中,会依次遍历该链表,并检查链表中的延迟函数是否调用了 recover 函数调用,若调用了则 panic 异常会被捕获而不会继续向上抛出,否则会继续向上抛出异常和执行延迟函数,直到该 panic 没有被捕获,进程异常终止,这个过程叫做panicking。我们需要知道的是即使panic被延迟函数链表中某个延迟函数捕获处理了,但其他的延迟函数还是会继续执行的,只是panic异常不在继续抛出。
|
|
|
接下来我们来将深入了解下panic和recover底层的实现机制。在开始之前,我们来看下下面的测试题。
|
|
|
测试题:下面哪些panic异常将会捕获? # case 1:
|
|
|
func main() { recover() panic("it is panic") // not recover } case 2:
|
|
|
func main() { defer func() { recover() }() panic("it is panic") // recover } case 3:
|
|
|
func main() { defer recover() panic("it is panic") // not recover } case 4:">
|
|
|
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
|
|
|
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#343a40">
|
|
|
<meta name="color-scheme" content="light dark"><meta property="og:title" content="panic与recover机制" />
|
|
|
<meta property="og:description" content="恐慌与恢复 - panic/recover # 我们知道Go语言中许多错误会在编译时暴露出来,直接编译不通过,但对于空指针访问元素,切片/数组越界访问之类的运行时错误,只会在运行时引发 panic 异常暴露出来。这种由Go语言自动的触发的 panic 异常属于运行时panic(Run-time panics)1。当发生 panic 时候,Go会运行所有已经注册的延迟函数,若延迟函数中未进行panic异常捕获处理,那么最终Go进程会终止,并打印堆栈信息。此外Go中还内置了 panic 函数,可以用于用户手动触发panic。
|
|
|
Go语言中内置的 recover 函数可以用来捕获 panic异常,但 recover 函数只能放在延迟函数调用中,才能起作用。我们从之前的章节《 基础篇-语言特性-defer函数 》了解到,多个延迟函数,会组成一个链表。Go在发生panic过程中,会依次遍历该链表,并检查链表中的延迟函数是否调用了 recover 函数调用,若调用了则 panic 异常会被捕获而不会继续向上抛出,否则会继续向上抛出异常和执行延迟函数,直到该 panic 没有被捕获,进程异常终止,这个过程叫做panicking。我们需要知道的是即使panic被延迟函数链表中某个延迟函数捕获处理了,但其他的延迟函数还是会继续执行的,只是panic异常不在继续抛出。
|
|
|
接下来我们来将深入了解下panic和recover底层的实现机制。在开始之前,我们来看下下面的测试题。
|
|
|
测试题:下面哪些panic异常将会捕获? # case 1:
|
|
|
func main() { recover() panic("it is panic") // not recover } case 2:
|
|
|
func main() { defer func() { recover() }() panic("it is panic") // recover } case 3:
|
|
|
func main() { defer recover() panic("it is panic") // not recover } case 4:" />
|
|
|
<meta property="og:type" content="article" />
|
|
|
<meta property="og:url" content="https://go.cyub.vip/feature/panic-recover/" /><meta property="article:section" content="feature" />
|
|
|
|
|
|
|
|
|
<title>panic与recover机制 | 深入Go语言之旅</title>
|
|
|
<link rel="manifest" href="/manifest.json">
|
|
|
<link rel="icon" href="/favicon.png" >
|
|
|
<link rel="stylesheet" href="/book.min.f06572240ce28e67eb332ac5cf5d59a696c47ad4c6f700d5842c5ed93dd8ec77.css" integrity="sha256-8GVyJAzijmfrMyrFz11ZppbEetTG9wDVhCxe2T3Y7Hc=" crossorigin="anonymous">
|
|
|
<script defer src="/flexsearch.min.js"></script>
|
|
|
<script defer src="/en.search.min.17ed8785d618483565ce5458241250de0bb24d7b931b8b71446036ef43affd37.js" integrity="sha256-F+2HhdYYSDVlzlRYJBJQ3guyTXuTG4txRGA270Ov/Tc=" crossorigin="anonymous"></script>
|
|
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-BQ229RRTTX"></script>
|
|
|
<script>
|
|
|
var doNotTrack = false;
|
|
|
if (!doNotTrack) {
|
|
|
window.dataLayer = window.dataLayer || [];
|
|
|
function gtag(){dataLayer.push(arguments);}
|
|
|
gtag('js', new Date());
|
|
|
gtag('config', 'G-BQ229RRTTX', { 'anonymize_ip': false });
|
|
|
}
|
|
|
</script>
|
|
|
<!--
|
|
|
Made with Book Theme
|
|
|
https://github.com/alex-shpak/hugo-book
|
|
|
-->
|
|
|
|
|
|
</head>
|
|
|
<body dir="ltr">
|
|
|
<input type="checkbox" class="hidden toggle" id="menu-control" />
|
|
|
<input type="checkbox" class="hidden toggle" id="toc-control" />
|
|
|
<main class="container flex">
|
|
|
<aside class="book-menu">
|
|
|
<div class="book-menu-content">
|
|
|
|
|
|
<nav>
|
|
|
<h2 class="book-brand">
|
|
|
<a class="flex align-center" href="/"><img src="https://static.cyub.vip/images/202310/golang-480.png" alt="Logo" /><span>深入Go语言之旅</span>
|
|
|
</a>
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
<div class="book-search">
|
|
|
<input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" />
|
|
|
<div class="book-search-spinner hidden"></div>
|
|
|
<ul id="book-search-results"></ul>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://www.cyub.vip/" target="_blank" rel="noopener">
|
|
|
个人博客
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://github.com/cyub" target="_blank" rel="noopener">
|
|
|
Github主页
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
<li>
|
|
|
<a href="https://www.topgoer.cn/?ref=go.cyub.vip" target="_blank" rel="noopener">
|
|
|
地鼠文档
|
|
|
</a>
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ul>
|
|
|
<li>
|
|
|
<p><strong>
|
|
|
<a href="/">深入Go语言之旅</a></strong></p>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>准备篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/compiler/">编译流程</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/">分析工具</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/gdb/">GDB</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/dlv/">Delve</a></li>
|
|
|
<li>
|
|
|
<a href="/analysis-tools/go-buildin-tools/">Go 内置工具</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/go-assembly/">Go汇编</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>基础篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type/">数据类型与数据结构</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type/string/">字符串</a></li>
|
|
|
<li>
|
|
|
<a href="/type/array/">数组</a></li>
|
|
|
<li>
|
|
|
<a href="/type/slice/">切片</a></li>
|
|
|
<li>
|
|
|
<a href="/type/nil/">nil</a></li>
|
|
|
<li>
|
|
|
<a href="/type/empty_struct/">空结构体</a></li>
|
|
|
<li>
|
|
|
<a href="/type/pointer/">指针</a></li>
|
|
|
<li>
|
|
|
<a href="/type/map/">映射</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/function/">函数</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/function/first-class/">一等公民</a></li>
|
|
|
<li>
|
|
|
<a href="/function/call-stack/">函数调用栈</a></li>
|
|
|
<li>
|
|
|
<a href="/function/pass-by-value/">值传递</a></li>
|
|
|
<li>
|
|
|
<a href="/function/closure/">闭包</a></li>
|
|
|
<li>
|
|
|
<a href="/function/method/">方法</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/feature/">语言特性</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/feature/comma-ok/">逗号ok模式</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/for-range/">遍历 - for-range语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/defer/">延迟执行 - defer语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/select/">通道选择器 - select语法</a></li>
|
|
|
<li>
|
|
|
<a href="/feature/panic-recover/"class=active>恐慌与恢复 - panic/recover</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<p><strong>运行时篇</strong></p>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/concurrency/">并发编程</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/concurrency/memory-model/">内存模型</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/context/">上下文 - context</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/channel/">通道 - channel</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/atomic/">原子操作 - atomic</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-map/">并发Map - sync.Map</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-waitgroup/">等待组 - sync.WaitGroup</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-once/">一次性操作 - sync.Once</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-pool/">缓冲池 - sync.Pool</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-cond/">条件变量 - sync.Cond</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-mutex/">互斥锁 - sync.Mutex</a></li>
|
|
|
<li>
|
|
|
<a href="/concurrency/sync-rwmutex/">读写锁 - sync.RWMutex</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/gmp/">G-M-P调度机制</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/gmp/gmp-model/">调度机制概述</a></li>
|
|
|
<li>
|
|
|
<a href="/gmp/scheduler/">调度器</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/memory/">内存管理</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/memory/allocator/">内存分配器</a></li>
|
|
|
<li>
|
|
|
<a href="/memory/gc/">GC</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li>
|
|
|
<a href="/type-system/">类型系统</a>
|
|
|
<ul>
|
|
|
<li>
|
|
|
<a href="/type-system/type/">类型系统</a></li>
|
|
|
<li>
|
|
|
<a href="/type-system/interface/">接口</a></li>
|
|
|
<li>
|
|
|
<a href="/type-system/reflect/">反射</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
</aside>
|
|
|
|
|
|
<div class="book-page">
|
|
|
<header class="book-header">
|
|
|
|
|
|
<div class="flex align-center justify-between">
|
|
|
<label for="menu-control">
|
|
|
<img src="/svg/menu.svg" class="book-icon" alt="Menu" />
|
|
|
</label>
|
|
|
|
|
|
<strong>panic与recover机制</strong>
|
|
|
|
|
|
<label for="toc-control">
|
|
|
|
|
|
</label>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<article class="markdown"><h1 id="恐慌与恢复---panicrecover">
|
|
|
恐慌与恢复 - panic/recover
|
|
|
<a class="anchor" href="#%e6%81%90%e6%85%8c%e4%b8%8e%e6%81%a2%e5%a4%8d---panicrecover">#</a>
|
|
|
</h1>
|
|
|
<p>我们知道Go语言中许多错误会在编译时暴露出来,直接编译不通过,但对于空指针访问元素,切片/数组越界访问之类的运行时错误,只会在运行时引发 <code>panic</code> 异常暴露出来。这种由Go语言自动的触发的 <code>panic</code> 异常属于<strong>运行时panic(Run-time panics)</strong><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。当发生 <code>panic</code> 时候,Go会运行所有已经注册的延迟函数,若延迟函数中未进行panic异常捕获处理,那么最终Go进程会终止,并打印堆栈信息。此外Go中还内置了 <code>panic</code> 函数,可以用于用户手动触发<code>panic</code>。</p>
|
|
|
<p>Go语言中内置的 <code>recover</code> 函数可以用来捕获 <code>panic</code>异常,但 <code>recover</code> 函数只能放在延迟函数调用中,才能起作用。我们从之前的章节《<strong>
|
|
|
<a href="/feature/defer/">基础篇-语言特性-defer函数</a></strong> 》了解到,多个延迟函数,会组成一个链表。Go在发生panic过程中,会依次遍历该链表,并检查链表中的延迟函数是否调用了 <code>recover</code> 函数调用,若调用了则 <code>panic</code> 异常会被捕获而不会继续向上抛出,否则会继续向上抛出异常和执行延迟函数,直到该 <code>panic</code> 没有被捕获,进程异常终止,这个过程叫做<strong>panicking</strong>。我们需要知道的是<strong>即使panic被延迟函数链表中某个延迟函数捕获处理了,但其他的延迟函数还是会继续执行的,只是panic异常不在继续抛出</strong>。</p>
|
|
|
<p>接下来我们来将深入了解下panic和recover底层的实现机制。在开始之前,我们来看下下面的测试题。</p>
|
|
|
<h2 id="测试题下面哪些panic异常将会捕获">
|
|
|
测试题:下面哪些panic异常将会捕获?
|
|
|
<a class="anchor" href="#%e6%b5%8b%e8%af%95%e9%a2%98%e4%b8%8b%e9%9d%a2%e5%93%aa%e4%ba%9bpanic%e5%bc%82%e5%b8%b8%e5%b0%86%e4%bc%9a%e6%8d%95%e8%8e%b7">#</a>
|
|
|
</h2>
|
|
|
<p><strong>case 1:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> recover()
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// not recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p><strong>case 2:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
|
|
|
</span></span><span style="display:flex;"><span> recover()
|
|
|
</span></span><span style="display:flex;"><span> }()
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p><strong>case 3:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> recover()
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// not recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p><strong>case 4:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> recover()
|
|
|
</span></span><span style="display:flex;"><span> }()
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p><strong>case 5:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
|
|
|
</span></span><span style="display:flex;"><span> recover()
|
|
|
</span></span><span style="display:flex;"><span> }()
|
|
|
</span></span><span style="display:flex;"><span> }()
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// not recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p><strong>case 6:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">doRecover</span>()
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">doRecover</span>() {
|
|
|
</span></span><span style="display:flex;"><span> recover()
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"hello"</span>)
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span></code></pre></div><p><strong>case 7:</strong></p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">doRecover</span>()
|
|
|
</span></span><span style="display:flex;"><span> panic(<span style="color:#e6db74">"it is panic"</span>) <span style="color:#75715e">// recover
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">doRecover</span>() {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> recover()
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span></code></pre></div><p>简单说明下上面几个案例运行结果:</p>
|
|
|
<ul>
|
|
|
<li><code>case 1</code>中recover函数调用不是在defer延迟函数里面,肯定不会捕获panic异常。</li>
|
|
|
<li><code>case 2</code>中是panic异常捕获的标准操作,是可以捕获panic异常的,<code>case 6</code>跟<code>case 2</code>是一样的,只不过一个是匿名延迟函数,一个是具名延迟函数,同样可以捕获panic异常。</li>
|
|
|
<li><code>case 3</code>中recover函数作为延迟函数,没有在其他延迟函数中调用,它也是不起作用的。</li>
|
|
|
<li><code>case 4</code>中recover函数被一个延迟函数调用,且recover函数本身作为一个延迟函数,这个情况下也是可以正常捕获panic异常的,<code>case 7</code>跟<code>case 4</code>是一样的,只不过一个是匿名延迟函数,一个是具名延迟函数,同样可以捕获panic异常。</li>
|
|
|
<li><code>case 5</code>中尽管recover函数被延迟函数调用,但它却无法捕获panic异常。</li>
|
|
|
</ul>
|
|
|
<p>从上面案例中可以看出来,使用recover函数进行panic异常捕获,也要使用正确才能起作用。下面会分析源码,探讨panic-recover实现机制,也能更好帮助你理解为什么<code>case 2</code>,<code>case 4</code>可以起作用,而<code>case 3</code>和<code>case 5</code>为啥没有起作用。</p>
|
|
|
<h2 id="源码分析">
|
|
|
源码分析
|
|
|
<a class="anchor" href="#%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90">#</a>
|
|
|
</h2>
|
|
|
<p>我们先分析<code>case 2</code>案例,我们可以通过<code>go tool compile -N -l -S case2.go</code>获取
|
|
|
<a href="https://go.godbolt.org/z/713T1TvoG">汇编代码</a>,来查看panic和recover在底层真正的实现:</p>
|
|
|
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;">
|
|
|
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
|
|
|
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
|
|
|
</span><span style="background-color:#3c3d38"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
|
|
|
</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
|
|
|
</span><span style="background-color:#3c3d38"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
|
|
|
</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
|
|
|
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
|
|
|
</span></code></pre></td>
|
|
|
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
|
|
|
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>main_pc0:
|
|
|
</span></span><span style="display:flex;"><span> TEXT <span style="color:#e6db74">""</span>.main<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>, ABIInternal, $104-0
|
|
|
</span></span><span style="display:flex;"><span> MOVQ <span style="color:#f92672">(</span>TLS<span style="color:#f92672">)</span>, CX
|
|
|
</span></span><span style="display:flex;"><span> CMPQ SP, 16<span style="color:#f92672">(</span>CX<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> JLS main_pc113
|
|
|
</span></span><span style="display:flex;"><span> SUBQ $104, SP
|
|
|
</span></span><span style="display:flex;"><span> MOVQ BP, 96<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> LEAQ 96<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, BP
|
|
|
</span></span><span style="display:flex;"><span> MOVL $0, <span style="color:#e6db74">""</span>..autotmp_1+16<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> LEAQ <span style="color:#e6db74">""</span>.main.func1·f<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>, AX
|
|
|
</span></span><span style="display:flex;"><span> MOVQ AX, <span style="color:#e6db74">""</span>..autotmp_1+40<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> LEAQ <span style="color:#e6db74">""</span>..autotmp_1+16<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, AX
|
|
|
</span></span><span style="display:flex;"><span> MOVQ AX, <span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> CALL runtime.deferprocStack<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> TESTL AX, AX
|
|
|
</span></span><span style="display:flex;"><span> JNE main_pc97
|
|
|
</span></span><span style="display:flex;"><span> JMP main_pc69
|
|
|
</span></span><span style="display:flex;"><span>main_pc69:
|
|
|
</span></span><span style="display:flex;"><span> LEAQ type.string<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>, AX
|
|
|
</span></span><span style="display:flex;"><span> MOVQ AX, <span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> LEAQ <span style="color:#e6db74">""</span>..stmp_0<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>, AX
|
|
|
</span></span><span style="display:flex;"><span> MOVQ AX, 8<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex; background-color:#3c3d38"><span> CALL runtime.gopanic<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span>main_pc97:
|
|
|
</span></span><span style="display:flex;"><span> XCHGL AX, AX
|
|
|
</span></span><span style="display:flex;"><span> CALL runtime.deferreturn<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> MOVQ 96<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, BP
|
|
|
</span></span><span style="display:flex;"><span> ADDQ $104, SP
|
|
|
</span></span><span style="display:flex;"><span> RET
|
|
|
</span></span><span style="display:flex;"><span>main_pc113:
|
|
|
</span></span><span style="display:flex;"><span> NOP
|
|
|
</span></span><span style="display:flex;"><span> CALL runtime.morestack_noctxt<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> JMP main_pc0
|
|
|
</span></span><span style="display:flex;"><span>main_func1_pc0:
|
|
|
</span></span><span style="display:flex;"><span> TEXT <span style="color:#e6db74">""</span>.main.func1<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>, ABIInternal, $32-0
|
|
|
</span></span><span style="display:flex;"><span> MOVQ <span style="color:#f92672">(</span>TLS<span style="color:#f92672">)</span>, CX
|
|
|
</span></span><span style="display:flex;"><span> CMPQ SP, 16<span style="color:#f92672">(</span>CX<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> JLS main_func1_pc53
|
|
|
</span></span><span style="display:flex;"><span> SUBQ $32, SP
|
|
|
</span></span><span style="display:flex;"><span> MOVQ BP, 24<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> LEAQ 24<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, BP
|
|
|
</span></span><span style="display:flex;"><span> LEAQ <span style="color:#e6db74">""</span>..fp+40<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, AX
|
|
|
</span></span><span style="display:flex;"><span> MOVQ AX, <span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex; background-color:#3c3d38"><span> CALL runtime.gorecover<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> MOVQ 24<span style="color:#f92672">(</span>SP<span style="color:#f92672">)</span>, BP
|
|
|
</span></span><span style="display:flex;"><span> ADDQ $32, SP
|
|
|
</span></span><span style="display:flex;"><span> RET
|
|
|
</span></span><span style="display:flex;"><span>main_func1_pc53:
|
|
|
</span></span><span style="display:flex;"><span> NOP
|
|
|
</span></span><span style="display:flex;"><span> CALL runtime.morestack_noctxt<span style="color:#f92672">(</span>SB<span style="color:#f92672">)</span>
|
|
|
</span></span><span style="display:flex;"><span> JMP main_func1_pc0</span></span></code></pre></td></tr></table>
|
|
|
</div>
|
|
|
</div>
|
|
|
<p>从上面汇编代码中,可以看出 <code>panic</code> 函数底层实现 <code>runtime.gopanic</code>,<code>recover</code> 函数底层实现是 <code>runtime.gorecover</code>。</p>
|
|
|
<p>panic函数底层实现的 <code>runtime.gopanic</code> 源码如下:</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">gopanic</span>(<span style="color:#a6e22e">e</span> <span style="color:#66d9ef">interface</span>{}) {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getg</span>()
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#f92672">...</span> <span style="color:#75715e">// 一些判断当前g是否允许在用户栈,是否正在内存分配的代码,略
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">p</span> <span style="color:#a6e22e">_panic</span> <span style="color:#75715e">// panic底层数据结构是_panic
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">arg</span> = <span style="color:#a6e22e">e</span> <span style="color:#75715e">// e是panic函数的参数,对应case2中的: it is panic
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">link</span> = <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> = (<span style="color:#f92672">*</span><span style="color:#a6e22e">_panic</span>)(<span style="color:#a6e22e">noescape</span>(<span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#f92672">&</span><span style="color:#a6e22e">p</span>))) <span style="color:#75715e">// 将当前panic挂到g上面
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">atomic</span>.<span style="color:#a6e22e">Xadd</span>(<span style="color:#f92672">&</span><span style="color:#a6e22e">runningPanicDefers</span>, <span style="color:#ae81ff">1</span>) <span style="color:#75715e">// 记录正在执行panic的goroutine数量,防止main groutine返回时候,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 其他goroutine的panic栈信息未打印出来。@see https://github.com/golang/go/blob/go1.14.13/src/runtime/proc.go#L208-L220
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 对于open-coded defer实现的延迟函数,需要扫描FUNCDATA_OpenCodedDeferInfo信息,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 获取延迟函数的sp/pc信息,并创建_defer结构,将其插入gp._defer链表中
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 这是也是在defer函数章节中,提到的为啥open-coded defer提升了延迟函数的性能,而panic性能却降低的原因
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">addOneOpenDeferFrame</span>(<span style="color:#a6e22e">gp</span>, <span style="color:#a6e22e">getcallerpc</span>(), <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#a6e22e">getcallersp</span>()))
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> { <span style="color:#75715e">// 开始遍历defer链表
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 当延迟函数里面再次抛出panic或者调用runtime.Goexit时候,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 会再次进入同一个延迟函数,此时d.started已经设置为true状态
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">started</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> { <span style="color:#75715e">// 标记上一个_panic状态为aborted
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">aborted</span> = <span style="color:#66d9ef">true</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span> = <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">openDefer</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 对于非open-coded defer函数,我们需要将_defer从gp._defer链表中溢出去,防止继续重复执行
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">fn</span> = <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span> = <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">freedefer</span>(<span style="color:#a6e22e">d</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 标记当前defer开始执行,这样当g栈增长时候或者垃圾回收时候,可以更新defer的参数栈帧
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">started</span> = <span style="color:#66d9ef">true</span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 记录当前的_panic信息到_defer结构中,这样当该defer函数再次发生panic时候,可以标记d._panic为aborted状态
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span> = (<span style="color:#f92672">*</span><span style="color:#a6e22e">_panic</span>)(<span style="color:#a6e22e">noescape</span>(<span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#f92672">&</span><span style="color:#a6e22e">p</span>)))
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">done</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">true</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">openDefer</span> { <span style="color:#75715e">// 如果该延迟函数是open-coded defer函数
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">done</span> = <span style="color:#a6e22e">runOpenDeferFrame</span>(<span style="color:#a6e22e">gp</span>, <span style="color:#a6e22e">d</span>) <span style="color:#75715e">// 运行open-coded defer函数,如果当前栈下面没有其他延迟函数,则返回true
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">done</span> <span style="color:#f92672">&&</span> !<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">recovered</span> { <span style="color:#75715e">// 如果当前栈下面没有其他open-coded defer函数了,且panic也未recover,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 那么继续当前的open-coded defer函数的sp作为基址,继续扫描funcdata,获取open-coded defer函数。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 之所以这么做是因为open-coded defer里面也存在defer函数的情况,例如case4
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">addOneOpenDeferFrame</span>(<span style="color:#a6e22e">gp</span>, <span style="color:#ae81ff">0</span>, <span style="color:#66d9ef">nil</span>)
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {<span style="color:#75715e">// 非open-coded defer实现的defer函数
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// getargp返回其caller的保存callee参数的地址。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 之前介绍过了Go语言中函数调用约定,callee的参数存储,是由caller的栈空间提供。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">argp</span> = <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#a6e22e">getargp</span>(<span style="color:#ae81ff">0</span>)) <span style="color:#75715e">// 这里面p.argp保存的gopanic函数作为caller时候,保存callee参数的地址。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 之所以要_panic.argp保存gopanic的callee参数地址,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 这是因为调用gorecover会通过此检查其caller的caller是不是gopanic。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 这也是case5等不能捕获panic异常的原因。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 调用defer函数
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">reflectcall</span>(<span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">fn</span>), <span style="color:#a6e22e">deferArgs</span>(<span style="color:#a6e22e">d</span>), uint32(<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">siz</span>), uint32(<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">siz</span>))
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">argp</span> = <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reflectcall did not panic. Remove d.
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">d</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">throw</span>(<span style="color:#e6db74">"bad defer entry in panic"</span>)
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">_panic</span> = <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">pc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">pc</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">sp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">unsafe</span>.<span style="color:#a6e22e">Pointer</span>(<span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">sp</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">done</span> { <span style="color:#75715e">// 从gp._defer链表清除掉当前defer函数
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">fn</span> = <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span> = <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">freedefer</span>(<span style="color:#a6e22e">d</span>)
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">recovered</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> = <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&&</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">goexit</span> <span style="color:#f92672">&&</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">aborted</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// A normal recover would bypass/abort the Goexit. Instead,
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// we return to the processing loop of the Goexit.
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">sigcode0</span> = uintptr(<span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">sp</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">sigcode1</span> = uintptr(<span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">pc</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">mcall</span>(<span style="color:#a6e22e">recovery</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">throw</span>(<span style="color:#e6db74">"bypassed recovery failed"</span>) <span style="color:#75715e">// mcall should not return
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">atomic</span>.<span style="color:#a6e22e">Xadd</span>(<span style="color:#f92672">&</span><span style="color:#a6e22e">runningPanicDefers</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">done</span> { <span style="color:#75715e">// panic已经被recover处理掉了,那么移除掉上面通过addOneOpenDeferFrame添加到gp._defer中的open-coded defer函数。
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 因为这些open-coded defer是通过inline方式执行的,从gp._defer链表中移除掉,不影响它们继续的执行
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">d</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">prev</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">_defer</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">d</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">openDefer</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">started</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">prev</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_defer</span> = <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">prev</span>.<span style="color:#a6e22e">link</span> = <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">newd</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">freedefer</span>(<span style="color:#a6e22e">d</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">d</span> = <span style="color:#a6e22e">newd</span>
|
|
|
</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">prev</span> = <span style="color:#a6e22e">d</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">d</span> = <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> = <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">link</span> <span style="color:#75715e">// 无用代码,上面已经操作过了
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Aborted panics are marked but remain on the g.panic list.
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Remove them from the list.
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&&</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">aborted</span> {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> = <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>.<span style="color:#a6e22e">link</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">nil</span> { <span style="color:#75715e">// must be done with signal
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">sig</span> = <span style="color:#ae81ff">0</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Pass information about recovering frame to recovery.
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">sigcode0</span> = uintptr(<span style="color:#a6e22e">sp</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">sigcode1</span> = <span style="color:#a6e22e">pc</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">mcall</span>(<span style="color:#a6e22e">recovery</span>)
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">throw</span>(<span style="color:#e6db74">"recovery failed"</span>) <span style="color:#75715e">// mcall should not return
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> }
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">preprintpanics</span>(<span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>)
|
|
|
</span></span><span style="display:flex;"><span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fatalpanic</span>(<span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>) <span style="color:#75715e">// should not return
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">*</span>(<span style="color:#f92672">*</span><span style="color:#66d9ef">int</span>)(<span style="color:#66d9ef">nil</span>) = <span style="color:#ae81ff">0</span> <span style="color:#75715e">// not reached
|
|
|
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
|
|
|
</span></span></code></pre></div><p>对于基于open-coded defer方式实现的延迟函数中处理panic recover逻辑,比如addOneOpenDeferFrame,runOpenDeferFrame等函数,这里不再深究。这里主要分析通过链表实现的延迟函数中处理panic recover逻辑。</p>
|
|
|
<p>接下来我们看下recover函数底层实现<code>runtime.gorecover</code>源码</p>
|
|
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">gorecover</span>(<span style="color:#a6e22e">argp</span> <span style="color:#66d9ef">uintptr</span>) <span style="color:#66d9ef">interface</span>{} {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gp</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">getg</span>()
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">gp</span>.<span style="color:#a6e22e">_panic</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">p</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> <span style="color:#f92672">&&</span> !<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">goexit</span> <span style="color:#f92672">&&</span> !<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">recovered</span> <span style="color:#f92672">&&</span> <span style="color:#a6e22e">argp</span> <span style="color:#f92672">==</span> uintptr(<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">argp</span>) {
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">recovered</span> = <span style="color:#66d9ef">true</span>
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">arg</span>
|
|
|
</span></span><span style="display:flex;"><span> }
|
|
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
|
|
|
</span></span><span style="display:flex;"><span>}
|
|
|
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
|
|
|
<hr>
|
|
|
<ol>
|
|
|
<li id="fn:1">
|
|
|
<p>
|
|
|
<a href="https://go.dev/ref/spec#Run_time_panics">Go官方语法指南:运行时恐慌</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
|
|
|
</li>
|
|
|
</ol>
|
|
|
</div>
|
|
|
</article>
|
|
|
|
|
|
|
|
|
|
|
|
<footer class="book-footer">
|
|
|
|
|
|
<div class="flex flex-wrap justify-between">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="book-comments">
|
|
|
|
|
|
<script src="https://giscus.app/client.js" data-repo="cyub/go-1.14.13"
|
|
|
data-repo-id="MDEwOlJlcG9zaXRvcnkzMzc2ODUyMzQ=" data-category="Announcements"
|
|
|
data-category-id="DIC_kwDOFCCq8s4CZ3BC"
|
|
|
data-mapping="pathname" data-strict="0" data-emit-metadata="0"
|
|
|
data-input-position="bottom" data-reactions-enabled="0"
|
|
|
data-lang="zh-CN" data-theme="preferred_color_scheme" crossorigin="anonymous" async>
|
|
|
</script>
|
|
|
<noscript>Please enable JavaScript to view the comments powered by giscus.</noscript>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<label for="menu-control" class="hidden book-menu-overlay"></label>
|
|
|
</div>
|
|
|
|
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
</body>
|
|
|
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|