【Go】并发目录遍历

package main

import(
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

func walkDir(dir string, fileSizes chan<- int64){
	for _, entry := range dirents(dir){
		if entry.IsDir(){
			subDir := filepath.Join(dir, entry.Name())
			walkDir(subDir, fileSizes)
		}else{
			fileSizes <- entry.Size()
		}
	}
}

func dirents(dir string)[]os.FileInfo{
	entries, err := ioutil.ReadDir(dir)
	if err != nil{
		fmt.Fprintf(os.Stderr, "du1: %v\n", err)
		return nil
	}
	return entries
}

func main(){
	flag.Parse()
	roots := flag.Args()
	if len(roots) == 0{
		roots = []string{"."}
	}
	fileSizes := make(chan int64)
	go func(){
		for _, root := range roots{
			walkDir(root, fileSizes)
		}
		close(fileSizes)
	}()
	var nfiles, nbytes int64
	for size := range fileSizes{
		nfiles++
		nbytes += size
	}
	printDiskUsage(nfiles, nbytes)
}

func printDiskUsage(nfiles, nbytes int64){
	fmt.Printf("%d files %.1fGB\n", nfiles, float64(nbytes)/1e9)
}

通过定义fileSizes通道,阻塞主线程,计算大小

通过一个线程不断遍历文件目录,传输大小给fileSizes

总共使用2个线程

package main

import(
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"
)

func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64){
	defer n.Done()
	for _, entry := range dirents(dir){
		if entry.IsDir(){
			n.Add(1)
			subDir := filepath.Join(dir, entry.Name())
			go walkDir(subDir, n, fileSizes)
		}else{
			fileSizes <- entry.Size()
		}
	}
}
var sema = make(chan struct{}, 20)
func dirents(dir string)[]os.FileInfo{
	sema <- struct{}{}
	defer func(){ <- sema }()
	entries, err := ioutil.ReadDir(dir)
	if err != nil{
		fmt.Fprintf(os.Stderr, "du1: %v\n", err)
		return nil
	}
	return entries
}

func main(){
	flag.Parse()
	roots := flag.Args()
	if len(roots) == 0{
		roots = []string{"."}
	}
	fileSizes := make(chan int64)
	var n sync.WaitGroup
	for _, root := range roots{
		n.Add(1)
		go walkDir(root, &n, fileSizes)
	}
	go func(){
		n.Wait()
		close(fileSizes)
	}()
	var tick <-chan time.Time
	tick = time.Tick(500*time.Millisecond)
	var nfiles, nbytes int64
loop:
		for{
			select{
			case size, ok := <-fileSizes:
				if !ok{
					break loop
				}
				nfiles++
				nbytes += size
			case <-tick:
				printDiskUsage(nfiles, nbytes)
			}
		}
	printDiskUsage(nfiles, nbytes)
}

func printDiskUsage(nfiles, nbytes int64){
	fmt.Printf("%d files %.1fGB\n", nfiles, float64(nbytes)/1e9)
}

版本2利用20个大小的缓冲通道作为信号量,最高打开20个并发遍历目录的线程。

通过一个线程,观察waitgroup的线程是否结束,结束则关闭fileSizes通道。

主线程通过time.Tick创建一个通道,周期性的触发tick事件,输出当前已经遍历的文件大小

主线程通过fileSizes通道计算已遍历文件大小

package main

import(
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"
)
var done = make(chan struct{})

func cancelled()bool{
	select{
	case <-done:
		return true
	default:
		return false
	}
}

func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64){
	defer n.Done()
	if cancelled(){
		return
	}
	for _, entry := range dirents(dir){
		if entry.IsDir(){
			n.Add(1)
			subDir := filepath.Join(dir, entry.Name())
			go walkDir(subDir, n, fileSizes)
		}else{
			fileSizes <- entry.Size()
		}
	}
}
var sema = make(chan struct{}, 20)
func dirents(dir string)[]os.FileInfo{
	select{
	case sema <- struct{}{}:
	case <-done:
		return nil
	}
	defer func(){ <- sema }()
	entries, err := ioutil.ReadDir(dir)
	if err != nil{
		fmt.Fprintf(os.Stderr, "du1: %v\n", err)
		return nil
	}
	return entries
}

func main(){
	flag.Parse()
	roots := flag.Args()
	if len(roots) == 0{
		roots = []string{"."}
	}

	go func(){
		os.Stdin.Read(make([]byte, 1))
		close(done)
	}()
	
	fileSizes := make(chan int64)
	var n sync.WaitGroup
	for _, root := range roots{
		n.Add(1)
		go walkDir(root, &n, fileSizes)
	}
	
	go func(){
		n.Wait()
		close(fileSizes)
	}()
	
	tick := time.Tick(500*time.Millisecond)
	var nfiles, nbytes int64
loop:
		for{
			select{
			case <-done:
				for range fileSizes{

				}
				return
			case size, ok := <- fileSizes:
				if !ok{
					break loop
				}
				nfiles++
				nbytes += size
			case <-tick:
				printDiskUsage(nfiles, nbytes)
			}
		}
	printDiskUsage(nfiles, nbytes)
}

func printDiskUsage(nfiles, nbytes int64){
	fmt.Printf("%d files %.1fGB\n", nfiles, float64(nbytes)/1e9)
}

版本3,通过增加一个done信号量和一个监控命令行输入的线程,终止程序运行。

canceled通过select实现非阻塞的通道。

各子线程通过done通道来决定是否结束线程。

其余一样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值