综合案例1

要求

1、开启一个writeData协程,向管道intChan中写入50个整数

2、开启一个readData的协程,从管道intChan中读取writeData写入的数据

3、注意: writeData和readDate操作的是同一个管道

4、主线程需要等待writeData和readData协程都完成工作才能退出

Golang学习(三十三) 管道综合案例_后端

思路分析

1、我们先定义两个管道"intChan(存储管道)"和"exitChan(退出管道)"

2、然后在主线程上开启两个协程"writeData"和"readData" 用于写/读管道数据

3、首先协程1"writeData"会将数据写入"intChan"管道,当写完数据后,将管道关闭

4、然后由协程2"readData"读取"intChan"管道数据,当无法读取数据时,写入true到退出管道"exitChan"

5、在协程执行中,主线程必须要有一个维持运行的程序,我们这里使用for循环做一个死循环

6、当退出队列为true时退出循环,表示为退出程序

案例

package main

import (
"fmt"
"math/rand"
"time"
)


func writeData(intChan chan int) { //管道是引用类型,不需要*接收
for i := 1; i <= 50; i++ {
intChan<- rand.Intn(50)
}
close(intChan) //当写完数据后,就关闭管道,

}


func readData(intChan chan int,exitChan chan bool) { //与写用同一个管道
//并且添加一个用于证明是否完成的管道

for {
v , ok := <-intChan //循环读取数据并验证是否读取成功
if !ok {
break //读取失败则退出
}
fmt.Printf("readData 读取到数据=%v",v)
}



exitChan<- true //读取数据完毕后为关闭通道添加一个true

close(exitChan) //然后关闭退出文档只允许读取

}

func main(){
rand.Seed(time.Now().UnixNano()) //使得随机数必定随机,不然随机不可用

intChan := make(chan int,50) //创建读写管道
exitChan := make(chan bool,1) //创建协程退出管道


go writeData(intChan) //启动两个协程,同时工作
go readData(intChan,exitChan)



for{ //主线程死循环等待退出管道的消息
_, ok := <-exitChan
if !ok{
break
}
}
}

综合案例2

1、 开启一个协程writeDataToFile ,随机生成1000个数据,存放到文件中

2、当writeDataToFil而完成写1000个数据到文件后,让sort协程从文件中读取1000个文件,并完成排序,重新写入到另外一个文件

3、 考察点: 协程和管道+文件的综合使用

4、功能扩展: 开启10个协程 writeDataToFile,每个协程随机生成1000个数据,放入到10个文件中

5、当10个文件都生成了,让10个sort协程从10个文件中读取1000个数据,并重新写入到10个结果文件

1、 开启一个协程writeDataToFile ,随机生成1000个数据,存放到文件中


先完成小功能,协程就是一个go的事情


package main

import (
"bufio"
"fmt"
"os"
)

func main(){
filePath := "e:/tar/test/1.txt" //设置文件路径
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
//写入模式,如果没有文件则创建,写入为追加数据模式
if err != nil{
fmt.Println(err)
return
}
defer file.Close() //关闭句柄



writer := bufio.NewWriter(file) //这里表示带缓存写入数据

writer.WriteString("写入的数据")

writer.Flush() //写入数据到磁盘
}


循环生成1000个数据,写入到该文件

我们将这个修改文件的代码设置为函数,接收路径和存放数据的值


package main

import (
"bufio"
"fmt"
"os"
)

func writeFile(filePath string,str string){
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
if err != nil{
fmt.Println(err)
return
}
defer file.Close()

writer := bufio.NewWriter(file)

writer.WriteString(str)

writer.Flush()
}


func main(){
//这里循环去调用10次函数去写入,和1000次差不多
//不过考虑到打开文件的操作过多,还是期望将所有的数据都放在一个遍历然后再做写入操作,但变量也有可能存在文件,持保留意见
for i := 1; i < 10; i++{
writeFile("e:/tar/test/1.txt",string(i) + "hahaha\r\n") // \r\n表示换行数据,后续添加的数据都是换行添加的
}
}

2、添加生成随机字符串函数

package main

import (
"bufio"
"fmt"
"math/rand"
"os"
)


func writeFile(filePath string,str string){
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
if err != nil{
fmt.Println(err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(str)
writer.Flush()
}


//设置一个函数用于生成随机字符串,返回一个string类型,使用的是math/rand包
func randSeq(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}



func main(){
for i := 1; i < 10; i++{
writeFile("e:/tar/test/1.txt",randSeq(10) + "\r\n")
//调用随机字符串函数randSeq时,需要指定获取的字符串的长度
//因为循环10次会调用10次函数,每次返回的值都不同
}
}


在生成的文本文件中可以看到以下随机字符串


Golang学习(三十三) 管道综合案例_数据_02

3、添加协程


上面说要调用一个协程writeDataToFile,这个writeDataToFile也是一个函数才对

并且我们需要判断这个协程是否退出了,保证主线程来运行


package main

import (
"bufio"
"fmt"
"math/rand"
"os"
)


func writeFile(filePath string,str string){
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
if err != nil{
fmt.Println(err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(str)
writer.Flush()
}


func randSeq(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}



func writeDataToFile(exitData chan bool){
for i := 1; i < 10; i++{
writeFile("e:/tar/test/1.txt",randSeq(10) + "\r\n")
}

exitData<-true //当for循环结束够写入到退出管道,并关闭退出管道
close(exitData)
}


func main(){
exitData := make(chan bool ,1) //设置一个退出管道

go writeDataToFile(exitData) //开启一个协程,传入退出管道

for {
_,ok := <-exitData //当管道没有关闭时,会爆deadlock,上面的true本身没有意义
//当true被写入后,管道被关闭,写入到ok的值全部为false
if !ok{ //一直false说明协程已经结束了,通过取反退出程序
break
}
}
}

4、让sort协程从文件中读取1000个数据,并完成排序,重新写入到另外一个文件


我们前面生成的是随机字符串,没有顺序概念,我们这里再添加一个随机生成数字的函数

替换掉前面的随机生成字符串函数,当然前面的函数还是保留,但是不调用


package main

import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"time"
)


func writeFile(filePath string,str string){
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
if err != nil{
fmt.Println(err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(str)
writer.Flush()
}


func randSeqString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}




func randSeqInt(n int) int{ //添加新函数,用于生成随机数字
rand.Seed(time.Now().UnixNano()) //加上这个才能真正随机,不然每次随机都相同
return rand.Intn(n)
}



func writeDataToFile(exitData chan bool){
for i := 1; i < 10; i++{
writeFile("e:/tar/test/1.txt", strconv.Itoa(randSeqInt(10))+ "\r\n") //这里通过strconv.Itoa 将返回的int转换为string 注意,直接用string会变成点点点
}
exitData<-true
close(exitData)
}


func main(){
exitData := make(chan bool ,1)
go writeDataToFile(exitData)

for {
_,ok := <-exitData
if !ok{
break
}
}

}

5、添加读取文件函数

package main

import (
"bufio"
"fmt"
"io"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"time"
)


func writeFile1(filePath string,str string){
file,err := os.OpenFile(filePath,os.O_WRONLY | os.O_CREATE | os.O_APPEND ,0666)
if err != nil{
fmt.Println(err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString(str)
writer.Flush()
}

func randSeqString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func randSeqInt(n int) int{
rand.Seed(time.Now().UnixNano()) //加上这个才能真正随机,不然每次随机都相同
return rand.Intn(n)
}

func writeDataToFile(exitData chan bool){
for i := 1; i < 10; i++{
writeFile1("e:/tar/test/1.txt", strconv.Itoa(randSeqInt(10))+ "\r\n") //这里通过strconv.Itoa 将返回的int转换为string
}
exitData<-true
close(exitData)
}




//添加读取文件的函数
func readFile() {
file ,err:= os.Open("E:/tar/test/1.txt")
if err != nil {
fmt.Println("打开文件失败")
}
defer file.Close()


var filedata []string
reader := bufio.NewReader(file)
for {
str,err := reader.ReadString('\n')
if err == io.EOF{
break
}
str = strings.Replace(str, " ", "", -1) //去除空格
str = strings.Replace(str, "\r\n", "", -1) //去除字符串中的\r\n值
filedata = append(filedata,str)
}





var fileintdata []int //不知道咋将string切片转换为int切片,我这里取出来,再新建一个int切片存储
for i,_ := range filedata{
int1,_ := strconv.Atoi(filedata[i]) //因为上面已经将我们添加的空格和换行都去掉了,这里我们能直接转为int值
fileintdata = append(fileintdata,int1)
}


sort.Ints(fileintdata) //这里通过sort包的Ints去帮我们把int切片排序


fmt.Println(fileintdata)
for _,v := range fileintdata{
fmt.Println(v)
writeFile1("e:/tar/test/2.txt", strconv.Itoa(v)+ "\r\n")
}

}







func main(){
exitData := make(chan bool ,1)
go writeDataToFile(exitData)

for {
_,ok := <-exitData
if !ok{
break
}
}


readFile() //调用读取函数


}