话说真的好久没有写博客了,最近赶新项目,工作太忙了。这一周任务比较少,又可以随便敲敲了。
逛论坛的时候突发奇想,想用go语言实现一个线程池,主要功能是:添加total个任务到线程池中,线程池开启number个线程,每个线程从任务队列中取出一个任务执行,执行完成后取下一个任务,全部执行完成后回调一个函数。
不知道有没有卵用,但是我尝试用它开启3个线程,下载10个文件,效果还是不错的。第一次写这方面的东西,可能写得不好。
思路就是把任务放到channel里,每个线程不停的从channel中取出任务执行,并把执行结果写入另一个channel,当得到total个结果后,回调函数。
上一发代码:
1 type GoroutinePool struct { 2 Queue chan func() error 3 Number int 4 Total int 5 6 result chan error 7 finishCallback func() 8 } 9 10 // 初始化 11 func (self *GoroutinePool) Init(number int, total int) { 12 self.Queue = make(chan func() error, total) 13 self.Number = number 14 self.Total = total 15 self.result = make(chan error, total) 16 } 17 18 // 开门接客 19 func (self *GoroutinePool) Start() { 20 // 开启Number个goroutine 21 for i := 0; i < self.Number; i++ { 22 go func() { 23 for { 24 task, ok := <-self.Queue 25 if !ok { 26 break 27 } 28 29 err := task() 30 self.result <- err 31 } 32 }() 33 } 34 35 // 获得每个work的执行结果 36 for j := 0; j < self.Total; j++ { 37 res, ok := <-self.result 38 if !ok { 39 break 40 } 41 42 if res != nil { 43 fmt.Println(res) 44 } 45 } 46 47 // 所有任务都执行完成,回调函数 48 if self.finishCallback != nil { 49 self.finishCallback() 50 } 51 } 52 53 // 关门送客 54 func (self *GoroutinePool) Stop() { 55 close(self.Queue) 56 close(self.result) 57 } 58 59 // 添加任务 60 func (self *GoroutinePool) AddTask(task func() error) { 61 self.Queue <- task 62 } 63 64 // 设置结束回调 65 func (self *GoroutinePool) SetFinishCallback(callback func()) { 66 self.finishCallback = callback 67 }
开启3个线程,下载10个文件的测试代码:
1 func Download_test() { 2 urls := []string{ 3 "http://dlsw.baidu.com/sw-search-sp/soft/44/17448/Baidusd_Setup_4.2.0.7666.1436769697.exe", 4 "http://dlsw.baidu.com/sw-search-sp/soft/3a/12350/QQ_V7.4.15197.0_setup.1436951158.exe", 5 "http://dlsw.baidu.com/sw-search-sp/soft/9d/14744/ChromeStandalone_V43.0.2357.134_Setup.1436927123.exe", 6 "http://dlsw.baidu.com/sw-search-sp/soft/6f/15752/iTunes_V12.2.1.16_Setup.1436855012.exe", 7 "http://dlsw.baidu.com/sw-search-sp/soft/70/17456/BaiduAn_Setup_5.0.0.6747.1435912002.exe", 8 "http://dlsw.baidu.com/sw-search-sp/soft/40/12856/QIYImedia_1_06_v4.0.0.32.1437470004.exe", 9 "http://dlsw.baidu.com/sw-search-sp/soft/42/37473/BaiduSoftMgr_Setup_7.0.0.1274.1436770136.exe", 10 "http://dlsw.baidu.com/sw-search-sp/soft/49/16988/YoudaoNote_V4.1.0.300_setup.1429669613.exe", 11 "http://dlsw.baidu.com/sw-search-sp/soft/55/11339/bdbrowserSetup-7.6.100.2089-1212_11000003.1437029629.exe", 12 "http://dlsw.baidu.com/sw-search-sp/soft/53/21734/91zhushoupc_Windows_V5.7.0.1633.1436844901.exe", 13 } 14 15 pool := new(GoroutinePool) 16 pool.Init(3, len(urls)) 17 18 for i := range urls { 19 url := urls[i] 20 pool.AddTask(func() error { 21 return download(url) 22 }) 23 } 24 25 isFinish := false 26 27 pool.SetFinishCallback(func() { 28 func(isFinish *bool) { 29 *isFinish = true 30 }(&isFinish) 31 }) 32 33 pool.Start() 34 35 for !isFinish { 36 time.Sleep(time.Millisecond * 100) 37 } 38 39 pool.Stop() 40 fmt.Println("所有操作已完成!") 41 } 42 43 func download(url string) error { 44 fmt.Println("开始下载... ", url) 45 46 sp := strings.Split(url, "/") 47 filename := sp[len(sp)-1] 48 49 file, err := os.Create("/Users/staff/Documents/Red_Test/AAAA/" + filename) 50 if err != nil { 51 return err 52 } 53 54 res, err := http.Get(url) 55 if err != nil { 56 return err 57 } 58 59 length, err := io.Copy(file, res.Body) 60 if err != nil { 61 return err 62 } 63 64 fmt.Println("## 下载完成! ", url, " 文件长度:", length) 65 return nil 66 }
使用起来还算可以的吧。。。