2022-5-30 实习Day47

1、手工分报-处理方式为空时,前端国际化提示为英文值,placeholder未匹配,问题查找与js代码修改–3小时 100%
2、汇出汇款提交再打回报异常,问题查找与调试修改。打回后提交先删后增,删除失败,定位mapper中的sql语句错误–3小时 100%

Golang编程学习(part 42)

1、channel应用案例1

请完成goroutine和channel协同工作的案例,具体要求:

  • 开启一个writeData协程,向管道intChan中写入50个整数
  • 开启一个readData协程,从管道intChan中读取writeData写入的数据
  • 注意:writeData和readData操作的是同一个管道
  • 主线程需要等待writeData和readData协程都完成工作才能退出

image-20220716093335699


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"fmt"
)

func main() {
// 创建两个管道
intChan := make(chan int, 100)
exitChan := make(chan bool, 1)

// 启动写、读协程
go writeData(intChan)
go readData(intChan, exitChan)

for {
_, ok := <-exitChan
if !ok {
break
}
}
}

func writeData(intChan chan int) {
for i := 0; i < 100; i++ {
// 放入数据
intChan <- i
fmt.Println("writeData ", i)
}
close(intChan)
}

func readData(intChan chan int, exitChan chan bool) {
// readData
for {
v, ok := <-intChan
if !ok {
break
}
fmt.Printf("readData 读到的数据=%v\n", v)
}

// readData读取完数据后即任务完成
exitChan <- true
close(exitChan)
}

2、channel应用案例2

  • 启动一个协程,将1-2000的数放入到一个channel中,比如numChan
  • 启动8个协程,从numChan取出数(比如n),并计算1+…+n的值,并存放到resChan
  • 最后8个协程协同完成工作后再遍历resChan,显示结果(如res[1]=1 .. res[10]=55 ..)
  • 注意:考虑resChan chan int是否合适
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
"fmt"
"strconv"
)

func main() {
intChan := make(chan int, 2000)
resData := make(chan string, 1000)
exitChan := make(chan bool, 8)

go writeDa(intChan)

for i := 0; i < 8; i++ {
go readDa(intChan, resData, exitChan)
}

go func() {
for i := 0; i < 8; i++ {
<-exitChan
}
// 当我们从exitChan中取出了8个结果,就可以放心的关闭
close(resData)

}()

for {
res, ok := <-resData
if !ok {
break
}
fmt.Println(res)
}
}

func writeDa(intChan chan int) {
for i := 1; i <= 2000; i++ {
intChan <- i
fmt.Println("写入了数据:", i)
}
close(intChan)
}

func readDa(intData chan int, resData chan string, exitChan chan bool) {
for {
result := 0
ints, ok := <-intData
if !ok {
break
}
for i := 1; i <= ints; i++ {
result += i
}
resData <- "result[" + strconv.Itoa(ints) + "]=" + strconv.Itoa(result)
}

exitChan <- true
}


3、goroutine+channel配合完成排序并写入文件

  • 开启一个协程 writeDataToFile,随机生成1000个数据,存放到文件中
  • 当writeDataToFile完成写1000个数据到文件后,让sort协程从文件中读取1000个数据,并完成排序,重新写入到另外一个文件
  • 考察点,协程和管道+文件到综合应用
  • 功能扩展:开10个协程writeDataToFile,每个协程随机生成1000个数据,存放到10个文件中
  • 当10个文件都生成了,让10个sort协程从文件中读取1000个文件,并完成排序,重新写入到10个结果文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// 未完成

package main

import (
"bufio"
"fmt"
"io"
"math/rand"
"os"
"strconv"
"strings"
"time"
)

func main() {

boolChannel := make(chan bool, 1)
filePathChannel := make(chan string, 1)

go writeDataToFile("mydoc", boolChannel, filePathChannel)
go sort(boolChannel, filePathChannel)

for {
_, ok := <-boolChannel

if !ok {
break
}

}
}

func sort(boolChannel chan bool, filePathChannel chan string) {
var file *os.File
var err error

for {
pathChannel, ok := <-filePathChannel
if !ok {
break
}
file, err = os.Open(pathChannel)
}

if err != nil {
fmt.Println(err)
return
}
reader := bufio.NewReader(file)

for {
readString, err := reader.ReadString('\n')
fmt.Println(strings.Trim(readString, "\n"))
if err == io.EOF {
break
}
}
}

func writeDataToFile(name string, boolChannel chan bool, filePathChannel chan string) {
getwd, _ := os.Getwd()
path := getwd + "/" + name + ".txt"
filePathChannel <- path
close(filePathChannel)

file, err := os.Create(path)
if err != nil {
fmt.Println(err)
}
defer file.Close()

writer := bufio.NewWriter(file)

rand.Seed(time.Now().UnixNano())
for i := 0; i < 1000; i++ {
intn := rand.Intn(1000)
writer.WriteString(strconv.Itoa(intn) + "\n")
}
writer.Flush()

boolChannel <- true
close(boolChannel)
}


4、应用案例—阻塞

1
2
3
4
5
6
7
8
9
10
11
12
13
func main(){
// 内容少往大空间放完全没问题
intChan := make(chan int, 10)
exitChan := make(chan bool, 1)

go writeData(intChan)
// go readData(intChan, exitChan) 就是为了管道流动起来,注释掉就会dead lock

// 就是为了等待...readData协程完成
for _= range exitChan{
fmt.Println("ok...")
}
}

问题:如果注销掉 go readData(intChan, exitChan),程序会怎么样?

答:如果只是向管道写数据,而没有读取,就会出现阻塞而dead lock,原因是intChan容量是10,而代码writeData会写入50个数据,因此会阻塞在writeData


5、应用案例—协程求素数

需求:

要求统计1-200000的数字中,哪些是素数?这个问题在本章开篇就提出了,现在我们有goroutine和channel的知识后,就可以完成了「测试数据:80000」

分析思路:

传统的方法,就是使用一个循环,循环的判断各个数是不是素数「ok」

使用并发/并行的方式,将统计素数的任务分配给多个(4个)goroutine去完成,更快!

image-20220721213820924


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package main

import (
"fmt"
"time"
)

func main() {

intChan := make(chan int, 1000)
primeChan := make(chan int, 2000)

// 标识退出的管道
exitChan := make(chan bool, 4) //4个

// 开启一个协程,向intChan放入1-8000个数
go putNum(intChan)

//开启4个协程,从intChan中取出数据,并判断是否为素数,如果是就放入到primeChan
for i := 0; i < 4; i++ {
go primeNum(intChan, primeChan, exitChan)
}

go func() {
for i := 0; i < 4; i++ {
<-exitChan
}
// 当我们从exitChan取出了4个结果,就可以放心的关闭primeChan
close(primeChan)
}()

// 遍历primeChan
for {

res, ok := <-primeChan
if !ok {
break
}
fmt.Println(res)
}

fmt.Println("main线程退出")
}

// 向intChan放入1-8000个数
func putNum(intChan chan int) {
for i := 0; i <= 8000; i++ {
intChan <- i
}
// 关闭intChan
close(intChan)
}

// 从intChan取出数据,并判断是否为素数,如果是就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
// 使用for循环
var flag bool
for {
time.Sleep(time.Millisecond * 10)
num, ok := <-intChan

if !ok {
// intChan取不到
break
}

// 假设是素数
flag = true
for i := 2; i < num; i++ {
// 说明该num不是素数
if num%i == 0 {
flag = false
break
}
}

if flag {
// 将这个数放到primeChan
primeChan <- num
}

}

fmt.Println("有一个primeNum协程因为取不到数据,退出")
//这里我们还不能关闭primeChan
exitChan <- true
}

结论:使用go协程后执行速度比普通方法提高至少4倍