杰克工作室 发表于 2023-7-24 15:33

go基础知识汇集

Go 语言数据类型包含基础类型和复合类型两大类。<br />
基础数据类型包括:布尔型、整型、浮点型、复数型、字符型、字符串型、错误类型。<br />
复合数据类型包括:指针、数组、切片、字典、通道、结构体、接口。<br />
<br />
Go 语言在声明变量时会默认给变量赋个当前类型的空值。<br />
<br />
一、_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。<br />
<br />
二、Go之所以会那么简洁,是因为它有一些默认的行为:<br />
&bull; 大写字母开头的变量是可导出的,也就是其它包可以读取的,是公用变量;小写字母开头的就是不可导出的,是私有变量。<br />
&bull; 大写字母开头的函数也是一样,相当于class中的带public关键词的公有函数;小写字母开头的就是有private关键词的私有函数。<br />
<br />
三、make、new操作<br />
make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配。<br />
内建函数new本质上说跟其它语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:new返回指针。<br />
内建函数make(T, args)与new(T)有着不同的功能,make只能创建slice、map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice为nil。对于slice、map和channel来说,make初始化了内部的数据结构,填充适当的值。<br />
make返回初始化后的(非零)值。<br />
<br />
四、总结:slice与array:<br />
1、数组是值类型,可以进行比较,可以将数组用作 map 的映射键。切片为引用类型,都不可以,不能比较,无法作为 map 的映射键。因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。<br />
2、数组有编译安全的检查,可以在早起就避免越界行为。切片是在运行时会出现越界的 panic,阶段不同。<br />
3、数组可以更好地控制内存布局,若拿切片替换,会发现不能直接在带有切片的结构中分配空间,数组可以。<br />
4、数组在访问单个元素时,性能比切片好。<br />
5、数组的长度,是类型的一部分。在特定场景下具有一定的意义。<br />
6、数组是切片的基础,每个数组都可以是一个切片,但并非每个切片都可以是一个数组。如果值是固定大小,可以通过使用数组来获得较小的性能提升(至少节省 slice 头占用的空间)。<br />
<br />
<br />
五、指针:<br />
指针的零值是nil。任何未初始化的指针都将具有该值nil:当指针没有指向的时候,不能对(*point)进行操作包括读取,否则会报空指针异常。解决方法即给该指针分配一个指向,即初始化一个内存,并把该内存地址赋予指针变量。Go不支持对指针进行算术运算。<br />
<br />
&nbsp;

杰克工作室 发表于 2023-7-24 16:15

<span style="color:#c0392b"><span style="font-size:16px">六:什么是 channel 管道</span></span><br />
<br />
它是一个数据管道,可以往里面写数据,从里面读数据。<br />
channel 是 goroutine 之间数据通信桥梁,而且是线程安全的。<br />
channel 遵循先进先出原则。<br />
<br />
写入,读出数据都会加锁。<br />
<br />
channel 可以分为 3 种类型:<br />
只读 channel,单向 channel<br />
只写 channel,单向 channel<br />
可读可写 channel
<table border="1" cellpadding="3" cellspacing="1">
        <thead>
                <tr>
                        <th>操作</th>
                        <th>nil的channel</th>
                        <th>正常channel</th>
                        <th>已关闭的channel</th>
                </tr>
        </thead>
        <tbody>
                <tr>
                        <td>读 &lt;-ch</td>
                        <td>阻塞</td>
                        <td>成功或阻塞</td>
                        <td>读到零值</td>
                </tr>
                <tr>
                        <td>写 ch&lt;-</td>
                        <td>阻塞</td>
                        <td>成功或阻塞</td>
                        <td>panic</td>
                </tr>
                <tr>
                        <td>关闭 close(ch)</td>
                        <td>panic</td>
                        <td>成功</td>
                        <td>panic</td>
                </tr>
        </tbody>
</table>

<p>&nbsp;</p>
channel 还可按是否带有缓冲区分为:<br />
带缓冲区的 channel,定义了缓冲区大小,可以存储多个数据<br />
不带缓冲区的 channel,只能存一个数据,并且只有当该数据被取出才能存下一个数据<br />
<br />
不带缓冲区channel示例:
<pre>
package main
import &quot;fmt&quot;
func main() {
&nbsp;&nbsp; &nbsp;ch := make(chan int) // 无缓冲的channel
&nbsp;&nbsp; &nbsp;go unbufferChan(ch)

&nbsp;&nbsp; &nbsp;for i := 0; i &lt; 5; i++ {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;receive &quot;, &lt;-ch) // 读出值
&nbsp;&nbsp; &nbsp;}
}
func unbufferChan(ch chan int) {
&nbsp;&nbsp; &nbsp;for i := 0; i &lt; 5; i++ {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;send &quot;, i)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ch &lt;- i // 写入值
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;println(&quot;write: &quot;, i)
&nbsp;&nbsp; &nbsp;}
}

运行结果如下:
send0
write:0
send1
receive0
receive1
write:1
send2
write:2
receive2
send3
write:3
receive3
send4
write:4
receive4

Program exited.</pre>

<p>带缓冲区的channel示例:</p>

<pre>
package main
import (
&nbsp;&nbsp; &nbsp;&quot;fmt&quot;
)
var c = make(chan int, 5)
func main() {
&nbsp;&nbsp; &nbsp;go worker(1)
&nbsp;&nbsp; &nbsp;for i := 1; i &lt; 10; i++ {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;c &lt;- i
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;write: &quot;, i)
&nbsp;&nbsp; &nbsp;}
}
func worker(id int) {
&nbsp;&nbsp; &nbsp;for {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;read: &quot;, &lt;-c)
&nbsp;&nbsp; &nbsp;}
}

运行结果:
write: &nbsp;1
write: &nbsp;2
write: &nbsp;3
write: &nbsp;4
write: &nbsp;5
write: &nbsp;6
read: &nbsp;1
read: &nbsp;2
read: &nbsp;3
read: &nbsp;4
read: &nbsp;5
read: &nbsp;6
read: &nbsp;7
write: &nbsp;7
write: &nbsp;8
write: &nbsp;9
想一想,为啥没有 read: 8, 9 的值呢?
</pre>

杰克工作室 发表于 2023-7-24 17:32

<p>判断 channel 是否关闭<br />
<br />
语法:v, ok := &lt;-ch<br />
说明:<br />
ok 为 true,读到数据,管道没有关闭<br />
ok 为 false,管道已关闭,没有数据可读<br />
示例:</p>

<pre>
package main
import (
&nbsp;&nbsp; &nbsp;&quot;fmt&quot;
)
func main() {
&nbsp;&nbsp; &nbsp;ch := make(chan int)
&nbsp;&nbsp; &nbsp;go test(ch)

&nbsp;&nbsp; &nbsp;for {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if v, ok := &lt;-ch; ok {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;get val: &quot;, v, ok)
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} else {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;break
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}
}

func test(ch chan int) {
&nbsp;&nbsp; &nbsp;for i := 0; i &lt; 5; i++ {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ch &lt;- i
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;fmt.Println(&quot;set val-----&quot;, i)
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;close(ch)//如果注释这一句会怎样呢?答案是deadlock
}
运行结果:
set val----- 0
get val: &nbsp;0 true
get val: &nbsp;1 true
set val----- 1
set val----- 2
get val: &nbsp;2 true
get val: &nbsp;3 true
set val----- 3
set val----- 4
get val: &nbsp;4 true

Program exited.
</pre>

<p>range 可以遍历数组,map,字符串,channel等。<br />
<br />
一个发送者可以关闭 channel,表明没有任何数据发送给这个 channel 了。接收者也可以测试channel是否关闭,通过 v, ok := &lt;-ch 表达式中的 ok 值来判断 channel 是否关闭。上一节已经说明 ok 为 false 时,表示 channel 没有接收任何数据,它已经关闭了。<br />
<br />
注意:仅仅只能是发送者关闭一个 channel,而不能是接收者。给已经关闭的 channel 发送数据会导致 panic。</p>

<div style="background:#eeeeee; border:1px solid #cccccc; padding:5px 10px"><span style="color:#c0392b">Note: channels 不是文件,你通常不需要关闭他们。那什么时候需要关闭?当要告诉接收者没有值发送给 channel 了,这时就需要了。<br />
比如终止 range 循环。<br />
<br />
当 for range或者无条件的for 遍历 channel 时,如果发送者没有关闭 channel 或在 range 之后关闭,都会导致 deadlock(死锁)。</span></div>

<p><br />
&nbsp;</p>

杰克工作室 发表于 2023-7-26 21:23

<p>操作不同状态的chan会引发的三种行为:</p>

<p><strong>Panic</strong></p>

<p>(1)向已经关闭的通道写数据会导致panic。</p>

<p>(2)重复关闭相同的通道会导致panic。</p>

<p><strong>阻塞</strong></p>

<p>(1)向未初始化的通道写数据或读数据都会导致当前goroutine的永久阻塞。</p>

<p>(2)向缓冲区已满的通道写入数据会导致goroutine阻塞。</p>

<p>(3)读取没有数据的通道会导致goroutine阻塞。</p>

<p><strong>非阻塞</strong></p>

<p>(1)读取已经关闭的通道不会引发阻塞,而是会得到该通道元素类型的零值。</p>

<p>(2)向有缓冲且没有满的通道读或写不会引发阻塞。</p>
页: [1]
查看完整版本: go基础知识汇集