教程集 www.jiaochengji.com
教程集 >  Golang编程  >  正文 Golang经典面试题上

Golang经典面试题上

发布时间:2021-12-10   编辑:jiaochengji.com
教程集为您提供Golang经典面试题上等资源,欢迎您收藏本站,我们将为您提供最新的Golang经典面试题上资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg><h3 id="1-写出下面代码输出内容">1. 写出下面代码输出内容</h3> <pre class="prettyprint"><code class="language-Go hljs erlang">package main <span class="hljs-function"><span class="hljs-title">import</span> <span class="hljs-params">( <span class="hljs-string">"fmt"</span> )</span> <span class="hljs-title">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> { <span class="hljs-title">defer_call</span><span class="hljs-params">()</span> } <span class="hljs-title">func</span> <span class="hljs-title">defer_call</span><span class="hljs-params">()</span> { <span class="hljs-title">defer</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> { <span class="hljs-title">fmt</span>.P<span class="hljs-title">rintln</span><span class="hljs-params">(<span class="hljs-string">"打印前"</span>)</span> }<span class="hljs-params">()</span> <span class="hljs-title">defer</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> { <span class="hljs-title">fmt</span>.P<span class="hljs-title">rintln</span><span class="hljs-params">(<span class="hljs-string">"打印中"</span>)</span> }<span class="hljs-params">()</span> <span class="hljs-title">defer</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> { <span class="hljs-title">fmt</span>.P<span class="hljs-title">rintln</span><span class="hljs-params">(<span class="hljs-string">"打印后"</span>)</span> }<span class="hljs-params">()</span> <span class="hljs-title">panic</span><span class="hljs-params">(<span class="hljs-string">"触发异常"</span>)</span> }</span></code></pre>

在这个案例中,<code>触发异常</code>这几个字打印的顺序其实是不确定的。<code>defer</code>, <code>panic</code>, <code>recover</code>一般都会配套使用来捕捉异常。先看下面的案例:

<ul><li>案例一</li></ul><pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">func</span> main() { defer_call() } <span class="hljs-keyword">func</span> defer_call() { <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { fmt.Println(<span class="hljs-string">"打印前"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { fmt.Println(<span class="hljs-string">"打印中"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { <span class="hljs-comment">// 必须要先声明defer,否则recover()不能捕获到panic异常</span> <span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>();err != <span class="hljs-constant">nil</span> { fmt.Println(err) <span class="hljs-comment">//err 就是panic传入的参数</span> } fmt.Println(<span class="hljs-string">"打印后"</span>) }() <span class="hljs-built_in">panic</span>(<span class="hljs-string">"触发异常"</span>) }</code></pre>

输出内容为:

<pre class="prettyprint"><code class=" hljs vhdl">触发异常 打印后 打印中 打印前 <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre> <ul><li>案例二</li></ul><pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">func</span> main() { defer_call() } <span class="hljs-keyword">func</span> defer_call() { <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { fmt.Println(<span class="hljs-string">"打印前"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { <span class="hljs-comment">// 必须要先声明defer,否则recover()不能捕获到panic异常</span> <span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>();err != <span class="hljs-constant">nil</span> { fmt.Println(err) <span class="hljs-comment">//err 就是panic传入的参数</span> } fmt.Println(<span class="hljs-string">"打印中"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { fmt.Println(<span class="hljs-string">"打印后"</span>) }() <span class="hljs-built_in">panic</span>(<span class="hljs-string">"触发异常"</span>) }</code></pre>

输出内容为:

<pre class="prettyprint"><code class=" hljs vhdl">打印后 触发异常 打印中 打印前 <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre> <ul><li>案例三</li></ul><pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">func</span> main() { defer_call() } <span class="hljs-keyword">func</span> defer_call() { <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { <span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>();err != <span class="hljs-constant">nil</span> { fmt.Println(err) <span class="hljs-comment">//err 就是panic传入的参数</span> } fmt.Println(<span class="hljs-string">"打印前"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { <span class="hljs-comment">// 必须要先声明defer,否则recover()不能捕获到panic异常</span> <span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>();err != <span class="hljs-constant">nil</span> { fmt.Println(err) <span class="hljs-comment">//err 就是panic传入的参数</span> } fmt.Println(<span class="hljs-string">"打印中"</span>) }() <span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() { <span class="hljs-keyword">if</span> err := <span class="hljs-built_in">recover</span>();err != <span class="hljs-constant">nil</span> { fmt.Println(err) <span class="hljs-comment">//err 就是panic传入的参数</span> } fmt.Println(<span class="hljs-string">"打印后"</span>) }() <span class="hljs-built_in">panic</span>(<span class="hljs-string">"触发异常"</span>) }</code></pre>

输出内容为:

<pre class="prettyprint"><code class=" hljs vhdl">触发异常 打印后 打印中 打印前 <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre>

总结:

<ol><li><code>defer</code>函数属延迟执行,延迟到调用者函数执行 <code>return</code> 命令前被执行。多个<code>defer</code>之间按<code>LIFO</code>先进后出顺序执行。</li><li><code>Go</code>中可以抛出一个<code>panic</code>的异常,然后在<code>defer</code>中通过<code>recover</code>捕获这个异常,然后正常处理。</li><li>如果同时有多个<code>defer</code>,那么异常会被最近的<code>recover()</code>捕获并正常处理。</li></ol><h3 id="2-以下代码有什么问题说明原因">2. 以下代码有什么问题,说明原因</h3> <pre class="prettyprint"><code class="language-Go hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">type</span> student <span class="hljs-keyword">struct</span> { Name <span class="hljs-typename">string</span> Age <span class="hljs-typename">int</span> } <span class="hljs-keyword">func</span> pase_student() <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]*student { m := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]*student) stus := []student{ {Name: <span class="hljs-string">"zhou"</span>, Age:<span class="hljs-number"> 24</span>}, {Name: <span class="hljs-string">"li"</span>, Age:<span class="hljs-number"> 23</span>}, {Name: <span class="hljs-string">"wang"</span>, Age:<span class="hljs-number"> 22</span>}, } <span class="hljs-keyword">for</span> _, stu := <span class="hljs-keyword">range</span> stus { m[stu.Name] = &stu } <span class="hljs-keyword">return</span> m } <span class="hljs-keyword">func</span> main() { students := pase_student() <span class="hljs-keyword">for</span> k, v := <span class="hljs-keyword">range</span> students { fmt.Printf(<span class="hljs-string">"key=%s,value=%v \n"</span>, k, v) } }</code></pre>

运行结果:

<pre class="prettyprint"><code class=" hljs livecodeserver">key=zhou,<span class="hljs-built_in">value</span>=&{wang <span class="hljs-number">22</span>} key=li,<span class="hljs-built_in">value</span>=&{wang <span class="hljs-number">22</span>} key=wang,<span class="hljs-built_in">value</span>=&{wang <span class="hljs-number">22</span>} Process finished <span class="hljs-operator">with</span> exit code <span class="hljs-number">0</span></code></pre>

修改一下代码:

将下面的代码:

<pre class="prettyprint"><code class=" hljs matlab"><span class="hljs-keyword">for</span> _, stu := range stus <span class="hljs-cell">{ m[stu.Name] = &stu }</span></code></pre>

修改为:

<pre class="prettyprint"><code class=" hljs perl"><span class="hljs-keyword">for</span> <span class="hljs-number">_</span>, stu := range stus { fmt.Printf(<span class="hljs-string">"<span class="hljs-variable">%v</span>\t<span class="hljs-variable">%p</span>\n"</span>,stu,&stu) <span class="hljs-keyword">m</span>[stu.Name] = &stu }</code></pre>

运行结果为:

<pre class="prettyprint"><code class=" hljs mathematica"><span class="hljs-list">{shen 24}</span> <span class="hljs-number">0xc4200a4020</span> <span class="hljs-list">{li 23}</span> <span class="hljs-number">0xc4200a4020</span> <span class="hljs-list">{wang 22}</span> <span class="hljs-number">0xc4200a4020</span> key=shen,value=&<span class="hljs-list">{wang 22}</span> key=li,value=&<span class="hljs-list">{wang 22}</span> key=wang,value=&<span class="hljs-list">{wang 22}</span> Process finished with exit code <span class="hljs-number">0</span></code></pre>

通过上面的案例,我们不难发现<code>stu</code>变量的地址始终保持不变,每次遍历仅进行<code>struct</code>值拷贝,故<code>m[stu.Name]=&stu</code>实际上一直指向同一个地址,最终该地址的值为遍历的最后一个<code>struct</code>的值拷贝。

形同如下代码:

<pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">var</span> stu student <span class="hljs-keyword">for</span> _, stu = <span class="hljs-keyword">range</span> stus { m[stu.Name] = &stu } </code></pre>

修正方案,取数组中原始值的地址:

<pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">for</span> i, _ := <span class="hljs-keyword">range</span> stus { stu:=stus[i] m[stu.Name] = &stu }</code></pre>

重新运行,效果如下:

<pre class="prettyprint"><code class=" hljs mathematica"><span class="hljs-list">{shen 24}</span> <span class="hljs-number">0xc42000a060</span> <span class="hljs-list">{li 23}</span> <span class="hljs-number">0xc42000a0a0</span> <span class="hljs-list">{wang 22}</span> <span class="hljs-number">0xc42000a0e0</span> key=shen,value=&<span class="hljs-list">{shen 24}</span> key=li,value=&<span class="hljs-list">{li 23}</span> key=wang,value=&<span class="hljs-list">{wang 22}</span> Process finished with exit code <span class="hljs-number">0</span></code></pre> <h3 id="3-下面的代码会输出什么并说明原因">3. 下面的代码会输出什么,并说明原因</h3> <pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> <span class="hljs-string">"runtime"</span> <span class="hljs-string">"sync"</span> ) <span class="hljs-keyword">func</span> init() { fmt.Println(<span class="hljs-string">"Current Go Version:"</span>, runtime.Version()) } <span class="hljs-keyword">func</span> main() { runtime.GOMAXPROCS<span class="hljs-number">(1</span>) count :=<span class="hljs-number"> 10</span> wg := sync.WaitGroup{} wg.Add(count *<span class="hljs-number"> 2</span>) <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> <span class="hljs-keyword">func</span>() { fmt.Printf(<span class="hljs-string">"[%d]"</span>, i) wg.Done() }() } <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> <span class="hljs-keyword">func</span>(i <span class="hljs-typename">int</span>) { fmt.Printf(<span class="hljs-string">"-%d-"</span>, i) wg.Done() }(i) } wg.Wait() }</code></pre>

运行效果:

<pre class="prettyprint"><code class=" hljs markdown">Current Go Version: go1.10.1 -9-[<span class="hljs-link_label">10</span>][<span class="hljs-link_reference">10</span>][<span class="hljs-link_label">10</span>][<span class="hljs-link_reference">10</span>][<span class="hljs-link_label">10</span>][<span class="hljs-link_reference">10</span>][<span class="hljs-link_label">10</span>][<span class="hljs-link_reference">10</span>][<span class="hljs-link_label">10</span>][<span class="hljs-link_reference">10</span>]-0--1--2--3--4--5--6--7--8- Process finished with exit code 0</code></pre>

两个<code>for</code>循环内部<code>go func</code> 调用参数i的方式是不同的,导致结果完全不同。这也是新手容易遇到的坑。

第一个<code>go func</code>中<code>i</code>是外部<code>for</code>的一个变量,地址不变化。遍历完成后,最终<code>i=10</code>。故<code>go func</code>执行时,<code>i</code>的值始终是<code>10</code>(<code>10</code>次遍历很快完成)。

第二个<code>go func</code>中<code>i</code>是函数参数,与外部<code>for</code>中的<code>i</code>完全是两个变量。尾部(<code>i</code>)将发生值拷贝,<code>go func</code>内部指向值拷贝地址。

<h3 id="4-下面代码会输出什么">4. 下面代码会输出什么?</h3> <pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span> <span class="hljs-keyword">type</span> People <span class="hljs-keyword">struct</span>{} <span class="hljs-keyword">func</span> (p *People) ShowA() { fmt.Println(<span class="hljs-string">"showA"</span>) p.ShowB() } <span class="hljs-keyword">func</span> (p *People) ShowB() { fmt.Println(<span class="hljs-string">"showB"</span>) } <span class="hljs-keyword">type</span> Teacher <span class="hljs-keyword">struct</span> { People } <span class="hljs-keyword">func</span> (t *Teacher) ShowB() { fmt.Println(<span class="hljs-string">"teacher showB"</span>) } <span class="hljs-keyword">func</span> main() { t := Teacher{} t.ShowA() }</code></pre>

运行结果如下:

<pre class="prettyprint"><code class=" hljs vhdl">showA showB <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre>

<code>Go</code>中没有继承,上面这种写法叫组合。

上面的<code>t.ShowA()</code>等价于<code>t.People.ShowA()</code>,将上面的代码修改如下:

<pre class="prettyprint"><code class=" hljs avrasm">func main() { t := Teacher{} t<span class="hljs-preprocessor">.ShowA</span>() fmt<span class="hljs-preprocessor">.Println</span>(<span class="hljs-string">"---------------"</span>) t<span class="hljs-preprocessor">.People</span><span class="hljs-preprocessor">.ShowA</span>() }</code></pre>

运行结果为:

<pre class="prettyprint"><code class=" hljs asciidoc">showA <span class="hljs-header">showB ---------------</span> showA showB Process finished with exit code 0</code></pre> <h3 id="5-下面代码会触发异常吗请详细说明">5. 下面代码会触发异常吗?请详细说明</h3> <pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">func</span> main() { runtime.GOMAXPROCS<span class="hljs-number">(1</span>) int_chan := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-typename">int</span>,<span class="hljs-number"> 1</span>) string_chan := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-typename">string</span>,<span class="hljs-number"> 1</span>) int_chan <-<span class="hljs-number"> 1</span> string_chan <- <span class="hljs-string">"hello"</span> <span class="hljs-keyword">select</span> { <span class="hljs-keyword">case</span> value := <-int_chan: fmt.Println(value) <span class="hljs-keyword">case</span> value := <-string_chan: <span class="hljs-built_in">panic</span>(value) } }</code></pre>

有可能会发生异常,如果没有<code>selct</code>这段代码,就会出现线程阻塞,当有<code>selct</code>这个语句后,系统会随机抽取一个<code>case</code>进行判断,只有有其中一条语句正常<code>return</code>,此程序将立即执行。

<h3 id="6-下面代码输出什么">6. 下面代码输出什么?</h3> <pre class="prettyprint"><code class=" hljs livecodeserver">package main import <span class="hljs-string">"fmt"</span> func calc(index <span class="hljs-keyword">string</span>, <span class="hljs-operator">a</span>, b int) int { ret := <span class="hljs-operator">a</span> b fmt.Println(index, <span class="hljs-operator">a</span>, b, ret) <span class="hljs-constant">return</span> ret } func main() { <span class="hljs-operator">a</span> := <span class="hljs-number">1</span> b := <span class="hljs-number">2</span> defer calc(<span class="hljs-string">"1"</span>, <span class="hljs-operator">a</span>, calc(<span class="hljs-string">"10"</span>, <span class="hljs-operator">a</span>, b)) <span class="hljs-operator">a</span> = <span class="hljs-number">0</span> defer calc(<span class="hljs-string">"2"</span>, <span class="hljs-operator">a</span>, calc(<span class="hljs-string">"20"</span>, <span class="hljs-operator">a</span>, b)) b = <span class="hljs-number">1</span> }</code></pre>

运行结果如下:

<pre class="prettyprint"><code class=" hljs vhdl"><span class="hljs-number">10</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> <span class="hljs-number">20</span> <span class="hljs-number">0</span> <span class="hljs-number">2</span> <span class="hljs-number">2</span> <span class="hljs-number">2</span> <span class="hljs-number">0</span> <span class="hljs-number">2</span> <span class="hljs-number">2</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">4</span> <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre>

在解题前需要明确两个概念:

<ul><li><code>defer</code>是在函数末尾的<code>return</code>前执行,先进后执行。</li><li>函数调用时 <code>int</code> 参数发生值拷贝。</li></ul>

不管代码顺序如何,<code>defer calc func</code>中参数<code>b</code>必须先计算,故会在运行到第三行时,执行<code>calc("10",a,b)</code>输出:<code>10 1 2 3</code>得到值<code>3</code>,将<code>cal("1",1,3)</code>存放到延后执执行函数队列中。

执行到第五行时,现行计算<code>calc("20", a, b)</code>即<code>calc("20", 0, 2)</code>输出:<code>20 0 2 2</code>得到值<code>2</code>,将<code>cal("2",0,2)</code>存放到延后执行函数队列中。

执行到末尾行,按队列先进后出原则依次执行:<code>cal("2",0,2)、cal("1",1,3)</code>,依次输出:<code>2 0 2 2、1 1 3 4</code> 。

<h3 id="7-请写出以下输入内容">7. 请写出以下输入内容</h3> <pre class="prettyprint"><code class=" hljs css"><span class="hljs-tag">package</span> <span class="hljs-tag">main</span> <span class="hljs-tag">import</span> "<span class="hljs-tag">fmt</span>" <span class="hljs-tag">func</span> <span class="hljs-tag">main</span>() <span class="hljs-rules">{ <span class="hljs-rule"><span class="hljs-attribute">s </span>:<span class="hljs-value">= <span class="hljs-function">make([]int, <span class="hljs-number">5</span>)</span> fmt.<span class="hljs-function">Printf(<span class="hljs-string">"%p\n"</span>, s)</span> s = <span class="hljs-function">append(s, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>)</span> fmt.<span class="hljs-function">Printf(<span class="hljs-string">"%p\n"</span>, s)</span> //new pointer fmt.<span class="hljs-function">Println(s)</span> </span></span></span>}</code></pre>

运行结果:

<pre class="prettyprint"><code class=" hljs vhdl"><span class="hljs-number">0xc4200180c0</span> <span class="hljs-number">0xc42001c0a0</span> [<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span>] <span class="hljs-keyword">Process</span> finished <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">0</span></code></pre> <h3 id="8-下面的代码有什么问题">8. 下面的代码有什么问题</h3> <pre class="prettyprint"><code class=" hljs go"> <span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> <span class="hljs-string">"sync"</span> ) <span class="hljs-keyword">type</span> UserAges <span class="hljs-keyword">struct</span> { ages <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span> sync.Mutex } <span class="hljs-keyword">func</span> (ua *UserAges) Add(name <span class="hljs-typename">string</span>, age <span class="hljs-typename">int</span>) { ua.Lock() <span class="hljs-keyword">defer</span> ua.Unlock() ua.ages[name] = age } <span class="hljs-keyword">func</span> (ua *UserAges) Get(name <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span> { <span class="hljs-keyword">if</span> age, ok := ua.ages[name]; ok { <span class="hljs-keyword">return</span> age } <span class="hljs-keyword">return</span><span class="hljs-number"> -1</span> } <span class="hljs-keyword">func</span> main() { count :=<span class="hljs-number"> 1000</span> gw := sync.WaitGroup{} gw.Add(count *<span class="hljs-number"> 3</span>) u := UserAges{ages: <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>{}} add := <span class="hljs-keyword">func</span>(i <span class="hljs-typename">int</span>) { u.Add(fmt.Sprintf(<span class="hljs-string">"user_%d"</span>, i), i) gw.Done() } <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> add(i) <span class="hljs-keyword">go</span> add(i) } <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> <span class="hljs-keyword">func</span>(i <span class="hljs-typename">int</span>) { <span class="hljs-keyword">defer</span> gw.Done() u.Get(fmt.Sprintf(<span class="hljs-string">"user_%d"</span>, i)) }(i) } gw.Wait() fmt.Println(<span class="hljs-string">"Done"</span>) }</code></pre>

输出结果:

<pre class="prettyprint"><code class=" hljs perl">fatal error: concurrent <span class="hljs-keyword">map</span> <span class="hljs-keyword">read</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">map</span> <span class="hljs-keyword">write</span> goroutine <span class="hljs-number">2022</span> [running]: runtime.throw(<span class="hljs-number">0x10c5472</span>, <span class="hljs-number">0x21</span>)</code></pre>

结论: 在执行 <code>Get</code> 方法时可能被<code>panic</code>。

虽然有使用<code>sync.Mutex</code>做写锁,但是<code>map</code>是并发读写不安全的。<code>map</code>属于引用类型,并发读写时多个协程见是通过指针访问同一个地址,即访问共享变量,此时同时读写资源存在竞争关系。所以会报错误信息:<code>fatal error: concurrent map read and map write</code>。

如果第一次没复现<code>panic</code>问题,可以再次运行,复现该问题。那么如何改善呢? 在<code>Go1.9</code>新版本中将提供并发安全的<code>map</code>。首先需要了解两种锁的不同:

<ul><li><code>sync.Mutex</code>互斥锁</li><li><code>sync.RWMutex</code>读写锁,基于互斥锁的实现,可以加多个读锁或者一个写锁。</li></ul>

RWMutex相关方法:

<pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">type</span> RWMutex <span class="hljs-keyword">func</span> (rw *RWMutex) Lock() <span class="hljs-keyword">func</span> (rw *RWMutex) RLock() <span class="hljs-keyword">func</span> (rw *RWMutex) RLocker() Locker <span class="hljs-keyword">func</span> (rw *RWMutex) RUnlock() <span class="hljs-keyword">func</span> (rw *RWMutex) Unlock()</code></pre>

代码改进如下:

<pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> <span class="hljs-string">"sync"</span> ) <span class="hljs-keyword">type</span> UserAges <span class="hljs-keyword">struct</span> { ages <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span> sync.RWMutex } <span class="hljs-keyword">func</span> (ua *UserAges) Add(name <span class="hljs-typename">string</span>, age <span class="hljs-typename">int</span>) { ua.Lock() <span class="hljs-keyword">defer</span> ua.Unlock() ua.ages[name] = age } <span class="hljs-keyword">func</span> (ua *UserAges) Get(name <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span> { ua.RLock() <span class="hljs-keyword">defer</span> ua.RUnlock() <span class="hljs-keyword">if</span> age, ok := ua.ages[name]; ok { <span class="hljs-keyword">return</span> age } <span class="hljs-keyword">return</span><span class="hljs-number"> -1</span> } <span class="hljs-keyword">func</span> main() { count :=<span class="hljs-number"> 10000</span> gw := sync.WaitGroup{} gw.Add(count *<span class="hljs-number"> 3</span>) u := UserAges{ages: <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>{}} add := <span class="hljs-keyword">func</span>(i <span class="hljs-typename">int</span>) { u.Add(fmt.Sprintf(<span class="hljs-string">"user_%d"</span>, i), i) gw.Done() } <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> add(i) <span class="hljs-keyword">go</span> add(i) } <span class="hljs-keyword">for</span> i :=<span class="hljs-number"> 0</span>; i < count; i { <span class="hljs-keyword">go</span> <span class="hljs-keyword">func</span>(i <span class="hljs-typename">int</span>) { <span class="hljs-keyword">defer</span> gw.Done() u.Get(fmt.Sprintf(<span class="hljs-string">"user_%d"</span>, i)) fmt.Print(<span class="hljs-string">"."</span>) }(i) } gw.Wait() fmt.Println(<span class="hljs-string">"Done"</span>) }</code></pre>

运行结果如下:

<pre class="prettyprint"><code class=" hljs asciidoc"><span class="hljs-bullet">. </span><span class="hljs-bullet">. </span><span class="hljs-bullet">. </span><span class="hljs-bullet">. </span>Done Process finished with exit code 0</code></pre> <h3 id="9-下面的迭代会有什么问题">9. 下面的迭代会有什么问题?</h3> <pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span> <span class="hljs-keyword">import</span> <span class="hljs-string">"sync"</span> <span class="hljs-keyword">import</span> <span class="hljs-string">"time"</span> <span class="hljs-keyword">type</span> ThreadSafeSet <span class="hljs-keyword">struct</span> { sync.RWMutex s []<span class="hljs-typename">int</span> } <span class="hljs-keyword">func</span> (set *ThreadSafeSet) Iter() <-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">interface</span>{} { ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">interface</span>{}) <span class="hljs-keyword">go</span> <span class="hljs-keyword">func</span>() { set.RLock() <span class="hljs-keyword">for</span> elem := <span class="hljs-keyword">range</span> set.s { ch <- elem fmt.Print(<span class="hljs-string">"get:"</span>, elem, <span class="hljs-string">","</span>) } <span class="hljs-built_in">close</span>(ch) set.RUnlock() }() <span class="hljs-keyword">return</span> ch } <span class="hljs-keyword">func</span> main() { <span class="hljs-comment">//read()</span> unRead() } <span class="hljs-keyword">func</span> read() { set := ThreadSafeSet{} set.s = <span class="hljs-built_in">make</span>([]<span class="hljs-typename">int</span>,<span class="hljs-number"> 100</span>) ch := set.Iter() closed := <span class="hljs-constant">false</span> <span class="hljs-keyword">for</span> { <span class="hljs-keyword">select</span> { <span class="hljs-keyword">case</span> v, ok := <-ch: <span class="hljs-keyword">if</span> ok { fmt.Print(<span class="hljs-string">"read:"</span>, v, <span class="hljs-string">","</span>) } <span class="hljs-keyword">else</span> { closed = <span class="hljs-constant">true</span> } } <span class="hljs-keyword">if</span> closed { fmt.Print(<span class="hljs-string">"closed"</span>) <span class="hljs-keyword">break</span> } } fmt.Print(<span class="hljs-string">"Done"</span>) } <span class="hljs-keyword">func</span> unRead() { set := ThreadSafeSet{} set.s = <span class="hljs-built_in">make</span>([]<span class="hljs-typename">int</span>,<span class="hljs-number"> 100</span>) ch := set.Iter() _ = ch time.Sleep<span class="hljs-number">(5</span> * time.Second) fmt.Print(<span class="hljs-string">"Done"</span>) } </code></pre>

结论:内部迭代出现阻塞。默认初始化时无缓冲区,需要等待接收者读取后才能继续写入。

chan在使用make初始化时可附带一个可选参数来设置缓冲区。默认无缓冲,题目中便初始化的是无缓冲区的chan,这样只有写入的元素直到被读取后才能继续写入,不然就一直阻塞。

设置缓冲区大小后,写入数据时可连续写入到缓冲区中,直到缓冲区被占满。从chan中接收一次便可从缓冲区中释放一次。可以理解为chan是可以设置吞吐量的处理池。

<blockquote>

<code>ch := make(chan interface{})</code>和 <code>ch := make(chan interface{},1)</code>是不一样的
无缓冲的 不仅仅是只能向 <code>ch</code> 通道放 一个值 而是一直要有人接收,那么<code>ch <- elem</code>才会继续下去,要不然就一直阻塞着,也就是说有接收者才去放,没有接收者就阻塞。

而缓冲为<code>1</code>则即使没有接收者也不会阻塞,因为缓冲大小是<code>1</code>只有当 放第二个值的时候 第一个还没被人拿走,这时候才会阻塞

</blockquote> <h3 id="10-以下代码能编译过去吗为什么">10. 以下代码能编译过去吗?为什么?</h3> <pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">type</span> People <span class="hljs-keyword">interface</span> { Speak(<span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> } <span class="hljs-keyword">type</span> Stduent <span class="hljs-keyword">struct</span>{} <span class="hljs-keyword">func</span> (stu *Stduent) Speak(think <span class="hljs-typename">string</span>) (talk <span class="hljs-typename">string</span>) { <span class="hljs-keyword">if</span> think == <span class="hljs-string">"bitch"</span> { talk = <span class="hljs-string">"You are a good boy"</span> } <span class="hljs-keyword">else</span> { talk = <span class="hljs-string">"hi"</span> } <span class="hljs-keyword">return</span> } <span class="hljs-keyword">func</span> main() { <span class="hljs-keyword">var</span> peo People = Stduent{} think := <span class="hljs-string">"bitch"</span> fmt.Println(peo.Speak(think)) }</code></pre>

结论:编译失败,值类型 <code>Student{}</code> 未实现接口<code>People</code>的方法,不能定义为 <code>People</code> 类型。

两种正确修改方法:

<ul><li>方法一</li></ul><pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">type</span> People <span class="hljs-keyword">interface</span> { Speak(<span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> } <span class="hljs-keyword">type</span> Stduent <span class="hljs-keyword">struct</span>{} <span class="hljs-keyword">func</span> (stu Stduent) Speak(think <span class="hljs-typename">string</span>) (talk <span class="hljs-typename">string</span>) { <span class="hljs-keyword">if</span> think == <span class="hljs-string">"bitch"</span> { talk = <span class="hljs-string">"You are a good boy"</span> } <span class="hljs-keyword">else</span> { talk = <span class="hljs-string">"hi"</span> } <span class="hljs-keyword">return</span> } <span class="hljs-keyword">func</span> main() { <span class="hljs-keyword">var</span> peo People = Stduent{} think := <span class="hljs-string">"hi"</span> fmt.Println(peo.Speak(think)) }</code></pre> <ul><li>方法二</li></ul><pre class="prettyprint"><code class=" hljs go"><span class="hljs-keyword">package</span> main <span class="hljs-keyword">import</span> ( <span class="hljs-string">"fmt"</span> ) <span class="hljs-keyword">type</span> People <span class="hljs-keyword">interface</span> { Speak(<span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> } <span class="hljs-keyword">type</span> Stduent <span class="hljs-keyword">struct</span>{} <span class="hljs-keyword">func</span> (stu Stduent) Speak(think <span class="hljs-typename">string</span>) (talk <span class="hljs-typename">string</span>) { <span class="hljs-keyword">if</span> think == <span class="hljs-string">"bitch"</span> { talk = <span class="hljs-string">"You are a good boy"</span> } <span class="hljs-keyword">else</span> { talk = <span class="hljs-string">"hi"</span> } <span class="hljs-keyword">return</span> } <span class="hljs-keyword">func</span> main() { <span class="hljs-keyword">var</span> peo People = &Stduent{} think := <span class="hljs-string">"bitch"</span> fmt.Println(peo.Speak(think)) }</code></pre>

总结:指针类型的结构体对象可以同时调用结构体值类型和指针类型对应的方法。而值类型的结构体对象只能调用值类型对应的接口方法。

到此这篇关于“Golang经典面试题上”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
想系统学习GO语言(Golang
:first-child和:last-child学习笔记
go经典面试题:浅析接口
golang map key 正则表达_在GoLang中实现线程安全的字典
golang面试官:for select时,如果通道已经关闭会怎么样?如果select中只有一个case呢?
golang经典面试问题汇总
Go开篇
不要等离职了,才知道for select时,如果通道已经关闭会怎么样?
golang for循环_golang面试官:for select时,如果通道已经关闭会怎么样?如果select中只有一个case呢?...
golang面试经之笔试2

[关闭]
~ ~