协程、管道和文件操作综合练习题
案例一:
要求:
//1.启动一个协程,将1-2000的数字放到channel(numChan)中
// 2.启动8个协程,从numChan取出数(n),并计算1+…+n的值,存放到resChan
// 3.最后8个协程协同完成工作后,再遍历resChan,显示结果如res[1]=1…res[10]=55
// 4.注意考虑resChan chan int是否合适?
代码:
package main
import (
"fmt"
"sync"
)
// 要求:1.启动一个协程,将1-2000的数字放到channel(numChan)中
// 2.启动8个协程,从numChan取出数(n),并计算1+..+n的值,存放到resChan
// 3.最后8个协程协同完成工作后,再遍历resChan,显示结果如res[1]=1...res[10]=55
// 4.注意考虑resChan chan int是否合适?
var (
n1 int = 0
account *int = &n1
lock sync.Mutex
)
func addUpper(n int, c1 chan map[int]int) {
resMap := make(map[int]int, 1)
res := 0
for i := 1; i <= n; i++ {
res += i
}
resMap[n] = res
c1 <- resMap
lock.Lock()
// 方法一
// if n == cap(c1) {
// close(c1)
// }
// 方法二
// 使用全局变量*account记录addUpper()运行次数,
// 为防止资源冲突,使用全局变量互斥锁
*account++
if *account == cap(c1) {
close(c1)
}
lock.Unlock()
}
func readData(c2 chan map[int]int, c3 chan bool) {
for {
v, ok := <-c2
if !ok {
break
}
fmt.Println("v=", v)
}
c3 <- true
close(c3)
}
func main() {
numchan := make(chan int, 2000)
// 1.
go func(c chan int) {
for i := 1; i <= 2000; i++ {
c <- i
}
close(c)
}(numchan)
// 2.
resChan := make(chan map[int]int, 2000)
for i := 0; i < 8; i++ {
for v := range numchan {
go addUpper(v, resChan)
}
}
// 3.
exitChan := make(chan bool, 1)
go readData(resChan, exitChan)
for {
if _, ok := <-exitChan; ok {
break
}
}
fmt.Println("读写操作完成")
}
案例二:
// 1)开一个协程 writeDataToFile,随机生成1000个数据,存放到文件
// 2)当writeDataFile 完成写入操作后,让sort协程从文件中读取1000个数据,
// 并完成排序,重新写入到另一个文件
// 3)考察点,协程+管道+文件的综合使用
// 4)功能扩展:开10个协程writeDataFile,每个协程随机生成1000个数据,存放放到10个文件中
// 5)当10个文件都生成了,让10个sort协程从10个文件中读取1000个数据,并完成排序,
// 并重新写入到10个新文件中
先完成前三个要求的代码:
package main
import (
"bufio"
"fmt"
"io"
"log"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"time"
)
// 要求:1)开一个协程 writeDataToFile,随机生成1000个数据,存放到文件
// 2)当writeDataFile 完成写入操作后,让sort协程从文件中读取1000个数据,
// 并完成排序,重新写入到另一个文件
// 3)考察点,协程+管道+文件的综合使用
// var lock sync.Mutex
func writeDataFile(c1 chan int, c2 chan bool) {
for i := 1; i <= 1000; i++ {
c1 <- rand.Intn(1000) + 1
}
close(c1)
filePath := "E:\\goproject\\src\\go_code\\chapter15\\homework\\case2\\write文件.txt"
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatal(err)
}
defer file.Close()
writer := bufio.NewWriter(file)
for {
v, ok := <-c1
if !ok {
break
}
str := strconv.Itoa(v) + "\n"
writer.WriteString(str)
}
writer.Flush()
c2 <- true
close(c2)
}
func sortData(c1 chan int, c2 chan bool) {
intSlice := make([]int, 0)
file, err := os.Open("E:\\goproject\\src\\go_code\\chapter15\\homework\\case2_1\\write文件.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
str, err1 := reader.ReadString('\n')
if err1 != nil {
if err1 == io.EOF {
break
}
log.Fatal(err1)
}
// 提取出的str字符串含有'\n',需要去除
// 采用strings.Trim(s string,cutset string)string
new_str := strings.Trim(str, "\n")
//将字符串转化为int 类型
// lock.Lock()
a, _ := strconv.ParseInt(new_str, 10, 64)
intSlice = append(intSlice, int(a))
// lock.Unlock()
}
sort.Ints(intSlice)
// 将排序好后的数据写入管道
for _, v := range intSlice {
c1 <- v
}
close(c1)
// 把管道中的数据写入新文件
filePath := "E:\\goproject\\src\\go_code\\chapter15\\homework\\case2_1\\sort文件.txt"
file1, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatal(err)
}
defer file1.Close()
writer := bufio.NewWriter(file1)
for {
v, ok := <-c1
if !ok {
break
}
str := strconv.Itoa(v) + "\n"
writer.WriteString(str)
// fmt.Println(str)
}
writer.Flush()
c2 <- true
close(c2)
}
func main() {
rand.Seed(time.Now().UnixNano())
intChan1 := make(chan int, 1000)
exitChan1 := make(chan bool, 1)
intChan2 := make(chan int, 10000)
exitChan2 := make(chan bool, 1)
go writeDataFile(intChan1, exitChan1)
for {
if _, ok := <-exitChan1; ok {
break
}
}
go sortData(intChan2, exitChan2)
for {
if _, ok := <-exitChan2; ok {
break
}
}
fmt.Println("test...")
}
扩展功能后的代码:
package main
import (
"bufio"
"fmt"
"io"
"log"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
)
// 4)功能扩展:开10个协程writeDataFile,每个协程随机生成1000个数据,存放放到10个文件中
// 5)当10个文件都生成了,让10个sort协程从10个文件中读取1000个数据,并完成排序,
// 并重新写入到10个新文件中
var (
n1 int = 0
account *int = &n1
n2 int = 0
p *int = &n2
lock sync.Mutex
)
func writeDataFile(j int, c1 chan int, c2 chan bool) {
// 每开辟一个协程,向管道中添加1000个随机数
for i := 1; i <= 1000; i++ {
c1 <- rand.Intn(1000) + 1
}
// 没开辟一个协程,建立一个新文件
filePath := "E:\\goproject\\src\\go_code\\chapter15\\homework\\case2_2\\write文件" + strconv.Itoa(j+1) + ".txt"
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 建立带缓存的writer
writer := bufio.NewWriter(file)
// 每个文件写入1000个数据
for i := 1; i <= 1000; i++ {
v, ok := <-c1
if !ok {
break
}
str := strconv.Itoa(v) + "\n"
writer.WriteString(str)
}
writer.Flush()
// 使用全局变量 *int类型 记录写入次数,当写入10次后,关闭管道
// 在通过退出标识管道 推入true ,让主程序结束阻塞
lock.Lock()
*account++
if *account == 10 {
close(c1)
if _, ok := <-c1; !ok {
c2 <- true
close(c2)
}
}
lock.Unlock()
}
func sortData(j int, c1 chan int, c2 chan bool) {
intSlice := make([]int, 0)
file, err := os.Open("E:\\goproject\\src\\go_code\\chapter15\\homework\\case2_2\\write文件" + strconv.Itoa(j+1) + ".txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
reader := bufio.NewReader(file)
for {
str, err1 := reader.ReadString('\n')
if err1 != nil {
if err1 == io.EOF {
break
}
log.Fatal(err1)
}
// 提取出的str字符串含有'\n',需要去除
// 采用strings.Trim(s string,cutset string)string
new_str := strings.Trim(str, "\n")
//将字符串转化为int 类型
// lock.Lock()
a, _ := strconv.ParseInt(new_str, 10, 64)
intSlice = append(intSlice, int(a))
// lock.Unlock()
}
sort.Ints(intSlice)
// 将排序好后的数据写入管道
// 出现问题:多个协程同时往管道写入内容,数据的顺序排序输入变得无意义
// 需要一个实现一个协程工作完后,管道才接受下一个协程的操作
//解决方方法使用了全局变量*P作为1000次写入的判断基准,为防止资源冲突
// 使用了全局变量互斥锁
for {
lock.Lock()
a := *p
lock.Unlock()
if a == 1000*j {
lock.Lock()
for _, v := range intSlice {
c1 <- v
*p++
}
// 把管道中的数据写入新文件
filePath := "E:\\goproject\\src\\go_code\\chapter15\\homework\\case2_2\\sort文件" + strconv.Itoa(j+1) + ".txt"
file1, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatal(err)
}
defer file1.Close()
writer := bufio.NewWriter(file1)
for i := 1; i <= 1000; i++ {
v, ok := <-c1
if !ok {
break
}
str := strconv.Itoa(v) + "\n"
writer.WriteString(str)
// fmt.Println(str)
}
writer.Flush()
lock.Unlock()
break
}
// lock.Unlock()
}
// 使用全局变量 *int类型 记录写入次数,当写入10次后,关闭管道
// 在通过退出标识管道 推入true ,让主程序结束阻塞
lock.Lock()
*account++
if *account == 20 {
close(c1)
if _, ok := <-c1; !ok {
c2 <- true
close(c2)
}
}
lock.Unlock()
}
func main() {
rand.Seed(time.Now().UnixNano())
intChan1 := make(chan int, 10000)
exitChan1 := make(chan bool, 1)
intChan2 := make(chan int, 10000)
exitChan2 := make(chan bool, 1)
for i := 0; i < 10; i++ {
go writeDataFile(i, intChan1, exitChan1)
}
for {
if _, ok := <-exitChan1; ok {
break
}
}
for i := 0; i < 10; i++ {
go sortData(i, intChan2, exitChan2)
}
for {
if _, ok := <-exitChan2; ok {
break
}
}
fmt.Println("test...")
}