先看看普通的递归搜索
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
谢谢大佬