【Go语言快速上手(五)】文件操作&协程操作-LMLPHP

1. 前言

本篇文章将会将GO语言的一大杀器,那就是协程. 为啥很多大厂都在慢慢的转GO.看完这篇文章你可能会有所感悟


2. GO语言的文件操作

这里就不多叙述什么是IO操作了,毕竟本系列的文章不是0基础. 首先最经典的IO操作函数,open和close:

【Go语言快速上手(五)】文件操作&协程操作-LMLPHP
【Go语言快速上手(五)】文件操作&协程操作-LMLPHP

package main
import (
	"bufio"
	"fmt"
	"os"
)
func main() {
	//打开文件
	 f, err := os.Open("test.txt")
	if err != nil{
		fmt.Println("打开文件失败
	}
	defer f.close() //函数执行完后关闭
	//创建一个流)
	reader := bufio.NewReader(f)
	//读取操作
	for{
		//以回车作为标识符,遇见\n后就是一次读取
		str.err := reader.ReadString("\n")
		if err!=io.EOF{
			break
		}
		fmt.Println(str)
	}
}

其实你对操作系统有一点理解的话,你应该能猜出来,所谓的GO语言的open,close,,newreader函数,无非就是封装了内几个系统调用

context,err := ioutil.ReadFile("文件路径")//返回值是[]byte,err
if err != nil{
	fmt.Println("读取出错",err)
}
fmt.Printf("%v",string(context))//将[]byte转换为string输出

还有一种打开文件的方法:

【Go语言快速上手(五)】文件操作&协程操作-LMLPHP
【Go语言快速上手(五)】文件操作&协程操作-LMLPHP

这和Linux下调用系统调用打开文件时,设置权限类似:

writer, err := os.OpenFile("test.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0)
	defer writer.Close()
	if err != nil {
		fmt.Println("openfile err")
		return
	}
	//一个流对应到一个文件上(管道)
	writefile := bufio.NewWriter(writer)
	//只是将数据写入了缓冲区,而还没有刷新到文件中
	writefile.WriteString("\nneokou will be the best")
	writefile.Flush()

2. 初识协程

【Go语言快速上手(五)】文件操作&协程操作-LMLPHP

func Routine(){
	for i:=0;i<10;i++{
		fmt.Println("hello routine")
		//阻塞一秒
		time.Sleep(time.Second*1)
	}
	func main(){
	go routine()
	for i:=0;i<10;i++{
		fmt.Println("hello main")
		time.Sleep(time.Second*1)
	}
}

3. 协程的等待问题

package main
import (
	"fmt"
	"sync"
	"time"
)
var wg sync.WaitGroup
var sum int

func add() {
	defer wg.Done() //函数执行完后,计数器减一
	for i := 0; i < 10000; i++ {
		sum++
	}
}
func main() {
	//设置计数为3代表要启动三个协程
	wg.Add(3)
	go add()
	go add()
	go add()
	wg.Wait()
	fmt.Println(sum)
}

当三个协程都执行完add函数后,计数器才会变成0,那么wg.Wait函数才会继续往后执行. 等待相关函数在sync包下,并且后面要讲解的加解锁函数也在sync包下


4. 协程的资源竞争问题

和线程一样,协程并发执行时也会有共享资源竞争的问题,这个问题的具体内容相信大家都是了解的,所以我们需要通过加解锁的方式来避免出现问题:

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup //定义waitgroup结构体
var sum int
var lock sync.Mutex //定义锁结构
func add() {
	defer wg.Done()
	for i := 0; i < 10000; i++ {
		lock.Lock()
		sum++
		lock.Unlock()
	}
}
func main() {
	wg.Wait() //阻塞等待协程计数器减为0 
	wg.Add(3)
	go add()
	go add()
	go add()
	wg.Wait()
	fmt.Println(sum)

}

【Go语言快速上手(五)】文件操作&amp;协程操作-LMLPHP

package main

import (
	"fmt"
	"sync"
	"time"
)
var rwlock sync.RWMutex //第一读写锁结构
var wg sync.WaitGroup
var sum int
func add() {
	defer wg.Done()
	for i := 0; i < 10000; i++ {
		rwlock.Lock() //lock默认为写锁枷锁
		sum++
		rwlock.Unlock()
	}
}
func read(n int) {
	defer wg.Done()
	rwlock.RLock() //读锁加锁
	fmt.Println("开始读取: ", n)
	time.Sleep(time.Second * 2)
	fmt.Println("读取成功 ", n)
	rwlock.RUnlock()
}
func main() {
	wg.Add(4)
	go add()
	go read(1)
	go read(2)
	go read(3)
	wg.Wait()
	fmt.Println(sum)
}


5. 总结以及拓展

其实协程的用法和线程非常类似,只不过它比线程更轻量,使用起来也更加的方便.所以说协程是GO语言的一大杀器, 直接使用go关键字就能开启协程. 这CPP看了都流泪了啊


04-29 13:48