[Golang]并发检索代码行数

先看看普通的递归搜索

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

func main() {
	path := "D:\\" //搜索路径
	key := ".go"   //文件名关键字
	t := time.Now()
	fmt.Println(myReadLine(path, key))
	fmt.Println(time.Since(t))
}

//path指当前搜索路径,sum指path路径下代码总行数
func myReadLine(path, key string) (sum int) {
	files, err := os.ReadDir(path) //打开目录
	if err == nil {
		for _, fileInfo := range files { //遍历目录下文件
			newPath := path + "\\" + fileInfo.Name()
			if fileInfo.IsDir() { //如果是文件夹就递归向下搜索
				sum += myReadLine(newPath, key)
			} else if strings.Contains(fileInfo.Name(), key) { //如果是符合要求的文件就打开
				file, err := os.Open(newPath)
				if err == nil {
					fileBuf := bufio.NewReader(file)
					lineSum := 0 //记录代码行数
					for {
						_, err := fileBuf.ReadString('\n') //读到'\n'才会停止,会包含'\n'
						lineSum++
						if err == io.EOF { //读到文件末尾就停止
							break
						}
					}
					file.Close() //不要忘了关闭文件
					sum += lineSum
				}
			}
		}
	}
	return sum //返回代码总数
}

而它的搜索结果和用时为:
在这里插入图片描述
这里造成它时间长的主要原因就是在递归时浪费了时间,我们可以想着将递归的部分交给其他的工人(协程)做,而自己只需要完成自己的任务即可,这样才能充分利用时间

并发搜索

因此我们这里可以引入一个控制中心来 1.控制工人们的任务分配(保证有工人时分配给工人,没工人就自己干),2.记录索索结果 的任务,这里通过channel来进行信息的传递,用select来接受各种类型通知
注意:goroutine不能开太多,协程的创建和销毁也十分消耗时间,所以我们需要控制限度(也就是工人总数)

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

const (
	key       = ".go" //检索关键字
	maxWorker = 32    //最大工人数
)

var (
	nowWorker     = 0                 //目前工作工人数
	countResult   = 0                 //结果总数
	foundResult   = make(chan int)    //发现可计数结果的通道
	searchRequest = make(chan string) //发现需要工人执行的任务的通道
	doneWork      = make(chan bool)   //通知工人完成工作的通道
)

func main() {
	path := "D:\\"
	nowWorker = 1 //目前占用一个工人执行启动工作
	t1 := time.Now()
	go search(path, true)
	waitingCenter()
	fmt.Println(countResult)
	fmt.Println(time.Since(t1))
}

func waitingCenter() { //控制中心
	for {              //一直监听各路消息
		select {
		case path := <-searchRequest: //接收到需求,分配任务
			nowWorker++
			go search(path, true)
		case <-doneWork: //工人完成工作,增加空闲工人数
			nowWorker--
			if nowWorker == 0 { //所有工人都完成了工作就over
				return
			} //所有工人都完成了任务,结束程序
		case cnt := <-foundResult: //发现可记录结果的通知
			countResult += cnt
		}
	}
}

//master说明此次执行是否是由工人(go)完成的,true由go完成,false递归实现
func search(path string, master bool) {
	files, err := os.ReadDir(path)
	if err == nil {
		for _, fileInfo := range files {
			newPath := path + "\\" + fileInfo.Name()
			if fileInfo.IsDir() { //发现任务通知控制中心
				if nowWorker < maxWorker { //有可用工人就用其他工人干
					searchRequest <- newPath
				} else { //没有多余工人就自己干
					search(newPath, false)
				}
			} else if strings.Contains(fileInfo.Name(), key) {
				file, err := os.Open(newPath)
				if err == nil {
					fileBuf := bufio.NewReader(file)
					lineSum := 0
					for {
						_, err := fileBuf.ReadString('\n') //读到'\n'才会停止,会包含'\n'
						lineSum++
						if err == io.EOF {
							break
						}
					}
					file.Close()
					foundResult <- lineSum
				}
			}
		}
	}
	if master { //一个任务完成了,如果是工人完成的就通知控制中心
		doneWork <- true
	}
}

它的执行结果为:
在这里插入图片描述
提升了快4倍,我的电脑存在8个逻辑CPU,理论可以提升8倍性能,但各种软件在运行时也会占用资源,所以无法达到理论最大值

本文思路来源于:https://www.bilibili.com/video/BV1qT4y1c77u
谢谢大佬

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值