go使用
<h2>设置镜像</h2>
<pre><code>go env -w GOPROXY=https://goproxy.cn,direct</code></pre>
<h2>关于 iota</h2>
<ul>
<li>
<p>第一步:不同 const 定义块互不干扰</p>
</li>
<li>
<p>第二步:所有注释行和空行全部忽略</p>
</li>
<li>
<p>第三步:没有表达式的常量定义复用上一行的表达式</p>
</li>
<li>第四步:从第一行开始,iota 从 0 逐行加一</li>
</ul>
<blockquote>
<p>参考文档:<a href="https://blog.wolfogre.com/posts/golang-iota/">https://blog.wolfogre.com/posts/golang-iota/</a></p>
</blockquote>
<h2>传递类型</h2>
<ol>
<li>
<p>数组、结构体都是值传递</p>
<p>因为结构体是值传递,所以如果方法的接受者是值,那么就操作的对象就是值的副本。(方法的接收者可以前移,所以相当于传递了一个struct值参数)。另外,如果结构体里有切片等引用类型,即使struct是值传递,但切片还是指向同一个对象</p>
</li>
<li>slice、ch、map 都是引用传递</li>
</ol>
<h2>slice 切片</h2>
<ol>
<li>slice底层是数组</li>
</ol>
<h2>ch 通道</h2>
<ol>
<li>
<p>同种类型的ch可以比较</p>
</li>
<li>
<p>被 close 后,再尝试发送就会宕机(已发的数据不影响),但是接受不会宕机,而且在收完缓存的数据后,会立即收到零值,可以用 <code>v, ok <- ch</code> 来判断是否通道已经读完一个关闭的通道(也可以用 <code>range</code> 来读取通道,就不用额外关心通道是否关闭)</p>
</li>
<li>
<p>缓冲</p>
<pre><code>ch = make(chan int) //无缓冲通道
ch = make(chan int, 0) //无缓冲通道</code></pre>
</li>
<li>
<p>单向通道</p>
<pre><code>chan<- int //只能发送的通道
<-chan int //只能接收的通道
// 注意:close()不能关闭只读通道;双向通道可以转换为单向甬道,反之则不行。</code></pre>
</li>
<li>
<p><code>cap(ch)</code> 可获取其缓冲区大小;<code>len(ch)</code> 可以获取当前通道内的元素个数</p>
</li>
<li>
<p><code>struct{}</code> 作为通道类型,它占用空间为 0</p>
</li>
<li>
<p><code>select</code> 只针对通道的读写动作</p>
</li>
<li>
<p>可以用关闭通道的特性,达到广播结束消息的效果</p>
</li>
<li>
<p><code>select</code> 可以将多个事件串行化,就可以避免加互斥锁</p>
</li>
<li>
<p>循环读取通道用 <code>rang</code>,<code>close</code> 时会自动结束</p>
</li>
<li>无缓冲通道,可以使流程同步化</li>
</ol>
<h2>同步机制</h2>
<ol>
<li>
<p>监控变量的 goroutine ,即用 routine 来负责变量的读写</p>
</li>
<li>
<p>避免竟态的方法:用 routine 监控变量;锁机制</p>
</li>
<li>
<p>在设计并发程序时,永远应该优先考虑清晰度,并且拒绝过早优化</p>
</li>
<li>
<p>封装就是隐藏数据,要封装一个对象,必须使用结构体</p>
</li>
<li>
<p>内存同步问题,包括编译器优化,和 cpu 缓存</p>
</li>
<li>defer 只支持函数,并且参数在定义 defer 的时候就已经取值了。defer 改变返回值:只支持定义了返回值变量的情况</li>
</ol>
<h2>其它</h2>
<ol>
<li>
<p>读取单个字节</p>
<pre><code>is.Stdin.Read(make([]byte, 1)) //读取单个字节</code></pre>
</li>
<li>struct 内嵌匿名字段(可以是指针类型),可使结构体直接使用内嵌字段的变量和方法。实际上,是编译器根据内嵌字段,来自动生成了外层结构体的方法。</li>
</ol>
<h2>常见问题</h2>
<p>1、协程泄露</p>
<pre><code>// 以下,由于 g2 超时退出,导致 g1 无法退出
func leak1() {
ch := make(chan int)
// g1
go func() {
time.Sleep(2 * time.Second) // 模拟 io 操作
ch <- 100 // 如果 g2 超时退出,这里 g1 就会阻塞住,永远无法退出
}()
// g2
// 阻塞住,直到超时或返回
select {
case <-time.After(500 * time.Millisecond):
fmt.Println("timeout! exit...")
case result := <-ch:
fmt.Printf("result: %d\n", result)
}
}
// 以下,由于没有关闭 ch,导致 g1 无法退出
func leak2() {
ch := make(chan int)
// 消费者 g1
go func() {
for result := range ch {
fmt.Printf("result: %d\n", result)
}
}()
// 生产者 g2
ch <- 1
ch <- 2
time.Sleep(time.Second) // 模拟耗时
fmt.Println("main goroutine g2 done...")
}</code></pre>
<h2>性能优化</h2>
<p><code>sync.Pool</code> 可以优化频繁创建、注销的对象。如用于解析 json 的 struct</p>
<blockquote>
<p>参考文档:<a href="https://geektutu.com/post/hpg-sync-pool.html">https://geektutu.com/post/hpg-sync-pool.html</a></p>
</blockquote>