Skip to content

Commit 337cd44

Browse files
authored
错别字更正和调整部分表述 (#62)
* Change stored value in concurrency example Updated stored value in atomic.Value example. * Fix spelling of 'atomic' in CAS explanation Corrected the spelling of 'atomic' in the CAS section and ensured consistency in the explanation of optimistic locking. * Refine test documentation for clarity and examples Updated text for clarity and consistency in examples. * Enhance CAS explanation and optimistic locking context Clarified the explanation of CAS and optimistic locking, emphasizing its role in concurrent programming and providing a clearer comparison with mutexes. * Refine array slicing and slice capacity explanations Updated examples and explanations regarding array slicing and slice capacity in Go.
1 parent 5bdd7b3 commit 337cd44

File tree

3 files changed

+18
-20
lines changed

3 files changed

+18
-20
lines changed

src/essential/base/60.slice.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,17 @@ cap(nums)
100100

101101
### 切割
102102

103-
切割数组的格式为`arr[startIndex:endIndex]`,切割的区间为**左闭右开**例子如下:
103+
切割数组的格式为`arr[startIndex:endIndex]`,切割的区间为**左闭右开**。且数组在切割后,就会变为切片类型。例子如下:
104104

105105
```go
106106
nums := [5]int{1, 2, 3, 4, 5}
107-
nums[1:] // 子数组范围[1,5) -> [2 3 4 5]
108-
nums[:5] // 子数组范围[0,5) -> [1 2 3 4 5]
109-
nums[2:3] // 子数组范围[2,3) -> [3]
110-
nums[1:3] // 子数组范围[1,3) -> [2 3]
111-
```
112107

113-
数组在切割后,就会变为切片类型
108+
nums[:] // 子切片范围[0,5) -> [1 2 3 4 5]
109+
nums[1:] // 子切片范围[1,5) -> [2 3 4 5]
110+
nums[:5] // 子切片范围[0,5) -> [1 2 3 4 5]
111+
nums[2:3] // 子切片范围[2,3) -> [3]
112+
nums[1:3] // 子切片范围[1,3) -> [2 3]
113+
```
114114

115115
```go
116116
func main() {
@@ -184,7 +184,7 @@ nums := new([]int) // 指针
184184

185185
::: tip
186186

187-
切片的底层实现依旧是数组,是引用类型,可以简单理解为是指向底层数组的指针。
187+
切片的底层实现依旧是数组,是引用类型,可以简单理解为是指向底层数组的指针(本质上切片在Go中是一个结构体,包含指向底层数组的指针、长度值、容量值)。因此切片作为函数参数传递时不复制底层数组,函数内对传入切片的修改会反映在原切片中
188188

189189
:::
190190

@@ -414,8 +414,7 @@ s2 := s1[3:4] // cap = 9 - 3 = 6
414414
```go
415415
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
416416
s2 := s1[3:4] // cap = 9 - 3 = 6
417-
// 添加新元素,由于容量为6.所以没有扩容,直接修改底层数组
418-
s2 = append(s2, 1)
417+
s2 = append(s2, 1) // 添加新元素,由于容量为6.所以没有扩容,直接修改底层数组
419418
fmt.Println(s2)
420419
fmt.Println(s1)
421420
```
@@ -433,8 +432,7 @@ fmt.Println(s1)
433432
func main() {
434433
s1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} // cap = 9
435434
s2 := s1[3:4:4] // cap = 4 - 3 = 1
436-
// 容量不足,分配新的底层数组
437-
s2 = append(s2, 1)
435+
s2 = append(s2, 1) // 容量不足,分配新的底层数组
438436
fmt.Println(s2)
439437
fmt.Println(s1)
440438
}

src/essential/senior/110.concurrency.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2302,7 +2302,7 @@ func main() {
23022302

23032303
### CAS
23042304

2305-
`atmoic` 包还提供了 `CompareAndSwap` 操作,也就是 `CAS`,它是乐观锁的一种典型实现。乐观锁本身并不是锁,是一种并发条件下无锁化并发控制方式。之所以被称作乐观锁,是因为它总是乐观的假设共享数据不会被修改,仅当发现数据未被修改时才会去执行对应操作,而前面了解到的互斥量就是悲观锁,互斥量总是悲观的认为共享数据肯定会被修改,所以在操作时会加锁,操作完毕后就会解锁。由于无锁化实现的并发安全效率相对于锁要高一些,许多并发安全的数据结构都采用了 `cAS` 来进行实现,不过真正的效率要结合具体使用场景来看。看下面的一个例子:
2305+
`atomic` 包还提供了 `CompareAndSwap` 操作,也就是 `CAS`。它是实现乐观锁和无锁数据结构的核心。乐观锁本身并不是锁,是一种并发条件下无锁化并发控制方式:线程/协程在修改数据前,不会先加锁,而是先读取数据,进行计算,然后在提交修改时使用`CAS`来判断在此期间是否有其他线程修改过该数据。如果没有(值仍等于之前读取的值),则修改成功;否则,失败并重试。因此之所以被称作乐观锁,是因为它总是乐观的假设共享数据不会被修改,仅当发现数据未被修改时才会去执行对应操作,而前面了解到的互斥量就是悲观锁,互斥量总是悲观的认为共享数据肯定会被修改,所以在操作时会加锁,操作完毕后就会解锁。由于无锁化实现的并发,其安全性和效率相对于锁要高一些,许多并发安全的数据结构都采用了 `CAS` 来进行实现,不过真正的效率要结合具体使用场景来看。看下面的一个例子:
23062306

23072307
```go
23082308
var lock sync.Mutex
@@ -2371,7 +2371,7 @@ func main() {
23712371
func main() {
23722372
var val atomic.Value
23732373
val.Store("hello world")
2374-
val.Store(114154)
2374+
val.Store(114514)
23752375
fmt.Println(val.Load())
23762376
}
23772377
// panic: sync/atomic: store of inconsistently typed value into Value

src/essential/senior/120.test.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ PASS
128128
ok golearn/test 0.038s
129129
```
130130

131-
上面三种情况虽然都完成了测试,但是输出结果太简介了,这时可以加上参数`-v`,来使输出结果更加详细一点,例如
131+
上面三种情况虽然都完成了测试,但是输出结果太简洁了,这时可以加上参数`-v`,来使输出结果更加详细一点,例如
132132

133133
```sh
134134
$ go test ./ -v
@@ -226,7 +226,7 @@ func ExampleWithDeadline() {
226226
}
227227
```
228228

229-
表面上看该测试函数就是一个普通的函数,不过示例测试主要是由`Output`注释来体现的,待测试函数只有一行输出时,使用`Output`注释来检测输出。首先创建一个`hello.go`的文化,写入如下代码
229+
表面上看该测试函数就是一个普通的函数,不过示例测试主要是由`Output`注释来体现的,待测试函数只有一行输出时,使用`Output`注释来检测输出。首先创建一个名为`hello.go`的文件,写入如下代码
230230

231231
```go
232232
package say
@@ -525,7 +525,7 @@ ok command-line-arguments 0.462s
525525

526526
### Helper
527527

528-
通过`t.Helper()`可以将当前函数标记为帮助函数,帮助函数不会单独作为一个测试用例用于执行,在记录日志时输出的行号也是帮助函数的调用者的行号,这样可以使得分析日志时定位更准确,避免的冗杂的其他信息。比如将上述`t.Cleanup`的例子就可以修改为帮助函数,如下。
528+
通过`t.Helper()`可以将当前函数标记为帮助函数,帮助函数不会单独作为一个测试用例用于执行,在记录日志时输出的行号也是帮助函数的调用者的行号,这样可以使得分析日志时定位更准确,避免的冗杂的其他信息。比如上述`t.Cleanup`的例子就可以修改为帮助函数,如下。
529529

530530
```go
531531
package tool_test
@@ -748,7 +748,7 @@ ok golearn/tool_test 0.450s
748748

749749
### 表格风格
750750

751-
在上述的单元测试中,测试的输入数据都是手动声明的一个个变量,当数据量小的时候无伤大雅,但如果想要测试多组数据时,就不太可能再去声明变量来创建测试数据,所以一般情况下都是尽量采用结构体切片的形式,结构体是临时声明的匿名结构体,因为这样的编码风格看起来就跟表格一样,所以称为`table-driven`下面举个例子,这是一个手动声明多个变量来创建测试数据的例子,如果有多组数据狠起来就不是很直观,所以将其修改为表格风格
751+
在上述的单元测试中,测试的输入数据都是手动声明的一个个变量,当数据量小的时候无伤大雅,但如果想要测试多组数据时,就不太可能再去声明变量来创建测试数据,所以一般情况下都是尽量采用结构体切片的形式,结构体是临时声明的匿名结构体,因为这样的编码风格看起来就跟表格一样,所以称为`table-driven`下面举个例子,这是一个手动声明多个变量来创建测试数据的例子,如果有多组数据看起来就不是很直观,所以将其修改为表格风格
752752

753753
```go
754754
func TestEqual(t *testing.T) {
@@ -1020,7 +1020,7 @@ geomean 543.6 541.7 -0.33%
10201020
² all samples are equal
10211021
```
10221022

1023-
从结果中可以看出 benchstat 将其分为了三组,分别是耗时,内存占用和内存分配次数,其中`geomoean`为平均值,`p`为 样本的显著性水平,临界区间通常为 0.05,高于 0.05 就不太可信,取其中一条数据如下:
1023+
从结果中可以看出 benchstat 将其分为了三组,分别是耗时,内存占用和内存分配次数,其中`geomean`为平均值,`p`为样本的显著性水平,临界区间通常为0.05,高于0.05就不太可信,取其中一条数据如下:
10241024

10251025
```
10261026
│ sec/op │ sec/op vs base │
@@ -1211,7 +1211,7 @@ func main() {
12111211
e4
12121212
```
12131213

1214-
究其原因在于 Go 在字符串单位是字节,而不是字符。所以再次修改待测源代码,如果传入的是非 utf8 字符串,直接返回错误
1214+
究其原因在于`\xe4`代表一个字节,但并不是一个有效的UTF-8序列(UTF-8编码中`\xe4`是一个三字节字符的开始,但后面缺少两个字节).转换成`[]rune`时,Golang自动将它变成含单个Unicode字符的`[]rune{"\uFFFD"}`,其反转后仍是`[]rune{"\uFFFD"}`,转换回`string`时该Unicode字符又被替换为其UTF-8编码`\xef\xbf\xbd`。因此一个解决办法是如果传入的是非 utf8 字符串,直接返回错误
12151215

12161216
```go
12171217
func Reverse(s string) (string, error) {

0 commit comments

Comments
 (0)