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

devtools/2024/9/23 0:01:01/

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:Go语言专栏⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习更多Go语言知识
  🔝🔝


在这里插入图片描述

GO快速上手

  • 1. 前言
  • 2. GO语言的文件操作
  • 2. 初识协程
  • 3. 协程的等待问题
  • 4. 协程的资源竞争问题
  • 5. 总结以及拓展

1. 前言

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

本章重点:

本篇文章会讲解GO语言中如何读写/创建文件,也就是我们所谓的IO流的用法. 然后, 会讲解协程的基本概念和GO语言中是如何操控协程的, 在协程的讲解中会穿插资源竞争下的锁问题,会讲解互斥锁和读写锁在GO语言中是如何使用的. 最后会给大家讲解GO语言中的特殊的类型: channel管道


2. GO语言的文件操作

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

在这里插入图片描述
在这里插入图片描述

请注意, open是函数,而close是方法,他们的用法是不同的,并且,open函数位于os包下,使用前记得import包进去. 除此之外,打开文件后不能直接读取内容,需要创建一个流,这个流你可以理解为是一条管道,连接被打开的文件和程序, 利用这条管道来读取文件中的数据

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函数,无非就是封装了内几个系统调用

还有一种写法可以不用使用open和close就能读取/写入文件:

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

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

在这里插入图片描述
在这里插入图片描述

这和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. 初识协程

在学习线程时,理解线程是轻量级的进程,使用多个线程完成不同的任务带来的损耗远比全部使用进程要少. 虽然线程已经够优秀了,但是进行上下文切换也会浪费很多时间, 于是引入了协程的概念, 协程可以理解为是微线程, 协程不像进程和线程一样需要进行上下文的切换, 协程的上下文切换由开发人员决定. 多的概念不说,GPT一搜遍地都是.总之协程是用来替代线程去完成高并发任务的

在这里插入图片描述

话不多说,直接上案例:

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)}
}

是的你没看错,这就是GO语言的大杀器,一个简单的关键字go,蕴含了多少功力? go后面加上一个函数,就是启动一个协程去执行此函数.除此之外,主线程退出后,协程也会跟着退出


3. 协程的等待问题

和线程甚至是进程一样, 协程启动后,主线程需要等待协程将代码执行完才能结束程序,否则一旦主线程退出,那么协程也会跟着退出,在Linux中的waitpid函数,或是pthread_join函数都是在做这一件事情. 而GO语言中的等待比较特殊, 通过add函数增加计数,通过done函数减少计数,而主程序等待计数器变为0后,就可以往后执行了

package main
import ("fmt""sync""time"
)
var wg sync.WaitGroup
var sum intfunc 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 mainimport ("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)}

相信有一点基础的你,看见这份代码是心里一笑,嘴角上扬,根本没有难度, 我们知道纯粹的互斥锁使用起来效率比较低,特别是在读多写少的场景下更是让人头疼,所以引入读写锁也是很有必要的.就是将读锁和写锁分开.如果你之前了解过读写锁,那么这些对你来说将会是小菜一碟

在这里插入图片描述

package mainimport ("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看了都流泪了啊


🔎 下期预告:管道,网络,反射 🔍

http://www.ppmy.cn/devtools/23136.html

相关文章

【极速前进】20240422:预训练RHO-1、合成数据CodecLM、网页到HTML数据集、MLLM消融实验MM1、Branch-Train-Mix

一、RHO-1&#xff1a;不是所有的token都是必须的 论文地址&#xff1a;https://arxiv.org/pdf/2404.07965.pdf 1. 不是所有token均相等&#xff1a;token损失值的训练动态。 ​ 使用来自OpenWebMath的15B token来持续预训练Tinyllama-1B&#xff0c;每1B token保存一个che…

C++ 指针

每一个变量都有一个内存位置&#xff0c;每一个内存位置都定义了可使用连字号&#xff08;&&#xff09;运算符访问的地址&#xff0c;它表示了在内存中的一个地址。请看下面的实例&#xff0c;它将输出定义的变量地址&#xff1a; #include <iostream>using namesp…

每天学习一个Linux命令之yumhistory

每天学习一个Linux命令之yumhistory 简介 在Linux系统中&#xff0c;yum是一款常用的软件包管理器。它能够简化软件包的安装、更新和删除等操作。yumhistory命令是yum包管理器中的一个子命令&#xff0c;它用于查看和管理yum交互式操作的历史记录。通过yumhistory命令&#x…

spring cloud eureka 初始化报错(A bean with that name has already been defined)

报错内容 The bean ‘eurekaRegistration’, defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration E u r e k a C l i e n t C o n f i g u r a t i o n . c l a s s ] , c o u l d n o t b e r e g i s t e r e d . A …

NDK 入门(二)—— 调音小项目

NDK 入门系列主要介绍 JNI 的相关内容&#xff0c;目录如下&#xff1a; NDK 入门&#xff08;一&#xff09;—— JNI 初探 NDK 入门&#xff08;二&#xff09;—— 调音小项目 NDK 入门&#xff08;三&#xff09;—— JNI 注册与 JNI 线程 NDK 入门&#xff08;四&#xff…

WPF2022终结版系列课程笔记 1 WPF 基本布局

本笔记为B站 微软系列技术教程 WPF项目实战合集(2022终结版) 项目记录 WPF 基本布局 WPF布局原则 一个窗口中只能包含一个元素 不应显示设置元素尺寸 不应使用坐标设置元素的位置 可以嵌套布局容器 WPF布局容器 StackPanel: 水平或垂直排列元素、Orientation属性分别: Hor…

挑战一周完成Vue3项目Day2:路由配置+登录模块+layout组件+路由鉴权

一、路由配置 经过分析&#xff0c;项目一共需要4个一级路由&#xff1a;登录&#xff08;login&#xff09;、主页&#xff08;home&#xff09;、404、任意路由&#xff08;重定向到404&#xff09;。 1、安装路由插件 pnpm install vue-router 2、创建路由组件 在src目…

【AIGC】探索大语言模型中的词元化技术机器应用实例

科技前沿&#xff1a;探索大语言模型中的词元化技术及其应用实例 一、词元化技术的原理与重要性二、词元化技术的应用实例与代码展示三、词元化技术在科技热点中的应用四、总结与展望 随着人工智能技术的迅猛发展&#xff0c;自然语言处理领域也取得了长足的进步。其中&#xf…