Golang-协程、管道和文件操作综合练习题

协程、管道和文件操作综合练习题

案例一:

要求:
//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...")
}
上一篇:漫话Redis源码之四十


下一篇:Golang面试题: 3个goroutine交替打印ABC