线程(goroutine)与管道(channel)的基本使用前面两篇文章已经介绍了,这篇文章介绍下多线程通讯与多线程操作管道。
实例
1.1、一个线程往管道里写数据、另一个线程从管道里读数据示例
package main import ( "fmt" "time" ) func writeChan(pi chan string) { for i := 0; i < 10; i++ { teststr := fmt.Sprintf("input channel data %s", string(i)) pi <- teststr } fmt.Println("write channel end len:", len(pi)) } func readChan(pi chan string) { var str string for i := 0; i < 10; i++ { str = <-pi fmt.Println("read channel data:", str) } } func main() { var strChan chan string // 定义管道 strChan = make(chan string, 10) // 初始化管道的长度 go writeChan(strChan) // 启动往管道里写数据的线程 go readChan(strChan) // 启动从管道里读数据的线程 time.Sleep(time.Second) // sleep 1秒,等两个线程结束 }
1.2、多线程判断一千以内的素数的例子
素数又叫质数,质数是指在大于1的自然数中,除了1和它本身以外,不能被其他自然数整除的数。最小的质数是2,它也是唯一的偶数质数,最前面的质数依次排列为:2、3、5、7、11、13、17、19、23、29、31等。”
package main import "fmt" func calc(taskChan chan int, resChan chan int, exitChan chan bool) { for v := range taskChan { flag := true for i :=2; i < v; i ++ { if v%i==0 { // 判断不是素数的内容 flag = false break } } if flag { resChan <- v // 将素数写入管道 } } fmt.Println("exit") exitChan <- true // 结束时将结束标记写入结束的管道 } func main() { // 定义三个管道,intChan里存1000个数字,在这些数字里判断素数 intChan := make(chan int, 1000) // resultChan里存判断出来的素数 resultChan := make(chan int, 1000) // exitChan里存储进程结束的信号,用来判断子进程是否都结束了 exitChan := make(chan bool, 8) go func() { // 启动一个线程往intChan管道里写入1000个数 for i :=0; i < 1000; i ++ { intChan <- i } // 1000个数写完后关闭管道(不能往管道里写数据了、用for range循环完intChan管道后会自动退出循环) close(intChan) }() // 启动8个携程执行计算素数的函数 for i :=0; i < 8; i ++ { go calc(intChan, resultChan, exitChan) // calc(数据管道, 结果管道, 退出信号管道) } // 启动一个线程,获取所有计算素数子进程的退出新号(从退出信号管道里获取八个退出信息号,证明八个goroutine全部退出了) go func() { for i := 0; i < 8; i ++ { <- exitChan // 只从管道里把内容取出来(不需要获取)可以这么写( <- exitChan),不用使用变量接,就是从管道里取出内容丢掉 fmt.Println("wait goroutine", i, "exited") } close(resultChan) // 所有计算素数的goroutine退出后关闭结果管道 }() // 循环读取计算后得到的素数(管道关掉之后for range循环完毕之后就会自动退出循环) for v := range resultChan { fmt.Println(v) }
1.3、使用管道判断多个子线程是否结束示例
package main import ( "fmt" ) // 往管道里写数据的函数 func senddata(ch chan int, exitChan chan struct{}) { // 往数据管道里写数据,写完后关闭管道 for i := 0; i < 10; i++ { ch <- i } close(ch) // 往数据管道里写完数据后往退出管道里写入退出新号 var a struct{} exitChan <- a } // 从管道里读数据的函数 func getdata(ch chan int, exitChan chan struct{}) { // 死循环读取管道里的数据 for { // 使用死循环读取管道里的数据时一定要通过类型断言判断管道关闭 v, ok := <-ch if !ok { fmt.Println("form chan get data failed") break } fmt.Println(v) } // 读取完数据后往退出管道里写入退出新号 var a struct{} exitChan <- a } func main() { // 初始化并实例化数据管道与结束信号管道 var pipe chan int pipe = make(chan int, 10) exitChan := make(chan struct{}, 3) // 启动一个往管道里写数据的子线程 go senddata(pipe, exitChan) // 启动一个从管道里读数据的子线程 go getdata(pipe,exitChan) // 判断两个子线程是否结束 var total = 0; for _ = range exitChan { total ++ if total == 2 { // 如果exitChan里有两个数则表示两个子线程结束了 break } } }