闲扯
2017年的最后一天啦!今天是最后一批90后(1999年12月31日出生)的18岁生日,祝他们生日快乐!明天就是2018年啦,意味着90后已经全部成年,逐步成为社会的中流砥柱啦!
顺便吐槽下,被朋友圈的18岁照片刷屏啦,岁月是把杀猪刀,18岁的少年们都去哪啦!小伙伴们不要只顾着工作,也注意下身体啊,要变强不要变秃啊!不要变成油腻的中年胖子啊!
祝自己在2018年能更加努力,锻炼好身体!还有很多很多事情等我去做!Fight!
言归正传,在Golang学习(一)那篇博客中我们写了一个小工具,当时还没学习到并发,正好元旦假期学习了下并发,来实践改进下我们的工具。
正文
遗留Bug修复
文件复制过程中异常报错,程序没有退出的问题已经修复,创建文件和打开文件时加入了panic
1 | srcFile, err := os.Open(src) |
2 | if err != nil { |
3 | fmt.Println(err) |
4 | panic("打开文件错误!") |
5 | } |
6 | defer srcFile.Close() |
7 | desFile, err := os.Create(des) |
8 | if err != nil { |
9 | fmt.Println(err) |
10 | panic("创建文件错误!") |
11 | } |
12 | defer desFile.Close() |
使用Golang的gorutine来并发,提高性能
创建一个容量与期望生成文件个数大小相当的布尔型的chan,然后循环执行任务,每次向chan写入一个值,并通过读取chan来阻塞main函数的结束,伪代码如下
1 | func main() { |
2 | c := make(chan bool, *generateFileNumber) |
3 | for count := 0; count < *generateFileNumber; count++ { |
4 | dosomething(c) |
5 | } |
6 | <-c |
7 | } |
8 | func dosomething(c) { |
9 | do |
10 | c <- true |
11 | } |
代码
1 | /* |
2 | * User: toddlerya |
3 | * Date: 2017/12/23 |
4 | * Update: 2017/12/31 |
5 | * ds接入模块加压工具 |
6 | */ |
7 | |
8 | package main |
9 | |
10 | import ( |
11 | "flag" |
12 | "fmt" |
13 | "io" |
14 | "math/rand" |
15 | "os" |
16 | "runtime" |
17 | "strconv" |
18 | "strings" |
19 | "time" |
20 | ) |
21 | |
22 | func judgeExists(name string) bool { |
23 | if _, err := os.Stat(name); err != nil { |
24 | if os.IsNotExist(err) { |
25 | return false |
26 | } |
27 | } |
28 | return true |
29 | } |
30 | |
31 | func copyFile(c chan bool, src, des string) (w int64, err error) { |
32 | srcFile, err := os.Open(src) |
33 | if err != nil { |
34 | fmt.Println(err) |
35 | panic("打开文件错误!") |
36 | } |
37 | defer srcFile.Close() |
38 | |
39 | desFile, err := os.Create(des) |
40 | if err != nil { |
41 | fmt.Println(err) |
42 | panic("创建文件错误!") |
43 | } |
44 | defer desFile.Close() |
45 | |
46 | c <- true |
47 | |
48 | return io.Copy(desFile, srcFile) |
49 | } |
50 | |
51 | func generateRandomNumber(start int, end int) int { |
52 | if end < 0 || start < 0 || (end-start) <= 0 { |
53 | fmt.Println("[-] 随机数起始值[start]必须大于等于0, 截至值[end]必须大于起始值!") |
54 | fmt.Printf("[-] 请检查配置是否正确: start=%v, end=%v\n", start, end) |
55 | panic("随机数参数错误") |
56 | } |
57 | rand.Seed(time.Now().UnixNano()) |
58 | num := rand.Intn((end - start)) + start |
59 | return num |
60 | } |
61 | |
62 | func main() { |
63 | runtime.GOMAXPROCS(runtime.NumCPU()) |
64 | srcFilePath := flag.String("s", "/home", "输入原始文件路径") |
65 | dstFilePath := flag.String("d", "/tmp", "目标输出路径") |
66 | renameFormat := flag.String("f", "abc-{random1}-456_780_{random2}.zip", "参数原始文件替换格式") |
67 | random1Start := flag.Int("r1s", 100, "随机参数1的起始值") |
68 | random1End := flag.Int("r1e", 999, "随机参数1的截止值") |
69 | random2Start := flag.Int("r2s", 100000, "随机参数2的起始值") |
70 | random2End := flag.Int("r2e", 999999, "随机参数2的截止值") |
71 | generateFileNumber := flag.Int("g", 10000, "需要生成的文件个数") |
72 | flag.Parse() |
73 | fmt.Printf("[srcFilePath] :%s\n", *srcFilePath) |
74 | fmt.Printf("[dstFilePath] :%s\n", *dstFilePath) |
75 | fmt.Printf("[renameFormat] :%s\n", *renameFormat) |
76 | fmt.Printf("[random1Start] :%v\n", *random1Start) |
77 | fmt.Printf("[random1End] :%v\n", *random1End) |
78 | fmt.Printf("[random2Start] :%v\n", *random2Start) |
79 | fmt.Printf("[random2End] :%v\n", *random2End) |
80 | fmt.Printf("[generateFileNumber] :%v\n", *generateFileNumber) |
81 | fmt.Println("=======================================") |
82 | t1 := time.Now() |
83 | if judgeExists(*srcFilePath) { |
84 | c := make(chan bool, *generateFileNumber) |
85 | fmt.Println("[+] 开始随机生成目标数据, 请注意", *dstFilePath, "目录是否有数据生成\t", "计划生成", *generateFileNumber, "个文件Orz") |
86 | for count := 0; count < *generateFileNumber; count++ { |
87 | random1Val := generateRandomNumber(*random1Start, *random1End) |
88 | random2Val := generateRandomNumber(*random2Start, *random2End) |
89 | temp := strings.Replace(*renameFormat, "{random1}", strconv.Itoa(random1Val), -1) |
90 | newFileName := strings.Replace(temp, "{random2}", strconv.Itoa(random2Val), -1) |
91 | dstJoinList := []string{*dstFilePath, newFileName} |
92 | newDstFilePath := strings.Join(dstJoinList, "/") |
93 | // fmt.Println("[*] 输出随机生成的文件: ", newDstFilePath) |
94 | go copyFile(c, *srcFilePath, newDstFilePath) |
95 | } |
96 | <-c |
97 | fmt.Println("[+] 已经完成目标: 共计生成", *generateFileNumber, "个文件") |
98 | } else { |
99 | fmt.Println("[-] 原始文件不存在, 请检查: ", *srcFilePath) |
100 | } |
101 | elapsed := time.Since(t1) |
102 | fmt.Println("运行耗时: ", elapsed) |
103 | } |
使用效果如下
没有使用gorutine之前
使用gorutine后的并发效果
异常处理
第一次自己写的代码遇到这个文件打开数过多的问题,还有点小激动呢!查看下系统当前的ulimit open files配置值
果断手动改大些(个人笔记本,不会经常进新大量IO操作,就临时改下吧)ulimit -n 65535
修改后的使用效果就是上面的并发测试那个图啦!
后续
其实还有一个使用了sync的版本,但是测试效果和没有使用并发的测试结果一样,甚至还要差一些,还是不懂的太多,留个坑,等搞明白了再来补充。
相关伪代码逻辑如下
1 | func main() { |
2 | wg := sync.WaitGroup{} |
3 | wg.Add(*generateFileNumber) |
4 | for count := 0; count < *generateFileNumber; count++ { |
5 | dosomething(&wg) |
6 | } |
7 | wg.Wait() |
8 | } |
9 | func dosomething(wg *sync.WaitGroup) { |
10 | do |
11 | wg.Done() |
12 | } |
13 | {% endcodeblock %} |