1.文件的打开与关闭
输入:
package main
import (
"fmt"
"os"
)
func main() {
//Open()函数
open, err := os.Open("E:/GO/notepad/0630/GoTest.txt")
//文件打开出错
if err != nil {
fmt.Println("open file err:", err)
} else {
fmt.Println("open file is:", open)
//Close()方法
errClose := open.Close()
if errClose != nil {
fmt.Println("errClose:", errClose)
}
fmt.Println("open file is closed")
}
}
输出:
open file is: &{0xc00006ca08}
open file is closed
2.ReadFile方法的使用(一次性读取的方式)
输入:
package main
import (
"fmt"
"os"
)
func main() {
//使用ReadFile方法,不需要再进行Open,Close,方法内部已经进行封装
file, err := os.ReadFile("E:/GO/notepad/0630/GoTest.txt")
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(file) //直接输出(字节类型)
fmt.Println(string(file)) //转成字符类型输出
}
输出:
[72 111 108 100 32 111 110]
Hold on
3.带缓冲的读取bufio.NewReader(file)
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("E:/GO/notepad/0630/GoTest.txt")
if err != nil {
fmt.Println("err=", err)
}
//所有程序执行完成后,进行文件的关闭
defer file.Close()
//创建文件流 利用NewReader(file)方法
buf := bufio.NewReader(file)
//进行读取操作
for {
readString, err := buf.ReadString('\n') //读取到 换行(字符用单引号) 结束读取
if err == io.EOF {
fmt.Println("文件读取完成")
break
}
fmt.Print(readString)
}
}
4.写入文件
const (
O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
O_RDWR int = syscall.O_RDWR // 读写模式打开文件
O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
)
输入:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//最后的0666在windows中设置不生效,在Linux中生效
//os.O_RDWR文件读写
//os.O_APPEND表示新添加的内容在原文件上进行追加
//os.O_CREATE表示如果文件不存在,进行创建
openFile, err := os.OpenFile("E:/GO/notepad/0630/GoTest2.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
s := os.FileMode(0666).String()
fmt.Println(s) //输出在Linux中的执行权限
if err != nil {
fmt.Println("err=", err)
return
}
defer openFile.Close()
//缓冲输入流
buf := bufio.NewWriter(openFile)
for i := 0; i < 10; i++ {
buf.WriteString("Hello xx\n")
}
//刷新数据
buf.Flush()
}
5.文件的复制
输入:
package main
import (
"fmt"
"os"
)
func main() {
//定义源文件
file, err := os.ReadFile("E:\\GO\\notepad\\0630\\GoTest2.txt")
if err != nil {
fmt.Println("Error:", err)
}
//写入文件
err = os.WriteFile("E:\\GO\\notepad\\0630\\GoTest3.txt", file, 0644)
if err != nil {
fmt.Println("Error:", err)
}
}
6.协程(实现多线程)
输入:
package main
import (
"fmt"
"time"
)
func test() {
for i := 0; i < 10; i++ {
fmt.Println("函数中的线程-----", i)
//阻塞一秒
time.Sleep(time.Second)
}
}
func main() {
go test() //开启协程
for i := 0; i < 10; i++ {
fmt.Println("主线程中的线程-----", i)
//阻塞一秒
time.Sleep(time.Second)
}
}
输出:
主线程中的线程----- 0
函数中的线程----- 0
函数中的线程----- 1
主线程中的线程----- 1
函数中的线程----- 2
主线程中的线程----- 2
函数中的线程----- 3
主线程中的线程----- 3
函数中的线程----- 4
主线程中的线程----- 4
主线程中的线程----- 5
函数中的线程----- 5
函数中的线程----- 6
主线程中的线程----- 6
主线程中的线程----- 7
函数中的线程----- 7
函数中的线程----- 8
主线程中的线程----- 8
主线程中的线程----- 9
函数中的线程----- 9
7.主死协从
主线程结束后,协程也立即结束
输入:
package main
import (
"fmt"
"time"
)
func test() {
for i := 0; i < 10; i++ {
fmt.Println("函数中的线程-----", i)
//阻塞一秒
time.Sleep(time.Second)
}
}
func main() {
go test() //开启协程
for i := 0; i < 3; i++ {
fmt.Println("主线程中的线程-----", i)
//阻塞一秒
time.Sleep(time.Second)
}
}
输出:
主线程中的线程----- 0
函数中的线程----- 0
主线程中的线程----- 1
函数中的线程----- 1
函数中的线程----- 2
主线程中的线程----- 2
8.多协程
输入:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
//匿名函数
go func(n int) {
fmt.Println("Hello World,", i)
}(i)
}
//防止主线程直接结束
time.Sleep(time.Second * 3)
}
输出:
Hello World, 4
Hello World, 0
Hello World, 2
Hello World, 1
Hello World, 3
9.WaitGroup
WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。
输入:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
for i := 0; i < 5; i++ {
wg.Add(1) //协程开始时加一
go func(n int) {
defer wg.Done() //协程结束时减一
fmt.Println(n)
}(i)
}
//协程结束前进行阻塞
wg.Wait()
}
输出:
4
2
1
3
0
10.互斥锁
Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。
func (*Mutex) Lock
func (m *Mutex) Lock()
Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。
func (*Mutex) Unlock
func (m *Mutex) Unlock()
Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。
输入:
package main
import (
"fmt"
"sync"
)
var account = 0
var wg = sync.WaitGroup{}
var l sync.Mutex //互斥锁
// 账户存钱函数
func save() {
defer wg.Done()
for i := 0; i < 10; i++ {
l.Lock() //上锁
account = account + i
l.Unlock() //去锁
}
}
// 账户取钱函数
func draw() {
defer wg.Done()
for i := 0; i < 10; i++ {
l.Lock() //上锁
account = account - i
l.Unlock() //去锁
}
}
func main() {
wg.Add(1)
save()
wg.Add(1)
draw()
wg.Wait()
fmt.Println(account)
}
输出:
0
11.读写锁
RWMutex是读写互斥锁。该锁可以被同时多个读取者持有或唯一个写入者持有。RWMutex可以创建为其他结构体的字段;零值为解锁状态。RWMutex类型的锁也和线程无关,可以由不同的线程加读取锁/写入和解读取锁/写入锁。
func (*RWMutex) Lock
func (rw *RWMutex) Lock()
Lock方法将rw锁定为写入状态,禁止其他线程读取或者写入。
func (*RWMutex) Unlock
func (rw *RWMutex) Unlock()
Unlock方法解除rw的写入锁状态,如果m未加写入锁会导致运行时错误。
func (*RWMutex) RLock
func (rw *RWMutex) RLock()
RLock方法将rw锁定为读取状态,禁止其他线程写入,但不禁止读取。
func (*RWMutex) RUnlock
func (rw *RWMutex) RUnlock()
Runlock方法解除rw的读取锁状态,如果m未加读取锁会导致运行时错误。
输入:
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
var l sync.RWMutex // 读写锁
func read() {
defer wg.Done()
l.RLock() //读取上锁,此时禁止其他线程写入
fmt.Println("执行读取")
time.Sleep(2 * time.Second)
fmt.Println("读取结束")
l.RUnlock()
}
func write() {
defer wg.Done()
l.Lock() //Lock方法将rw锁定为写入状态,禁止其他线程读取或者写入。
fmt.Println("执行写入")
time.Sleep(5 * time.Second)
fmt.Println("写入结束")
l.Unlock()
}
func main() {
for i := 0; i < 5; i++ {
wg.Add(1)
go read()
}
go write()
wg.Wait()
}
输出:
执行写入
写入结束
执行读取
执行读取
执行读取
执行读取
执行读取
读取结束
读取结束
读取结束
读取结束
12.管道
特点:
(1)管道本质就是一个队列(先进先出)
(2)自身线程安全,多协程访问时,不需要加锁,channel本身就是线程安全的
(3)管道有类型的,一个string的管道只能存放string类型数据
定义管道并初始化
输入:
func main() {
//定义一个管道
var intChan chan int
var strChan chan string
//通过make()方法进行初始化
intChan = make(chan int)
strChan = make(chan string)
//管道为引用数据类型
fmt.Printf("intChan => %v\n", intChan)
fmt.Printf("strChan => %v\n", strChan)
}
13.管道的关闭
package main
import "fmt"
func main() {
var intChin chan int
//定义缓冲通道
intChin = make(chan int, 5)
//向管道中存入数据
intChin <- 1
intChin <- 2
//管道关闭
close(intChin)
//读取管道数据
fmt.Println(<-intChin)//读取正常
fmt.Println(<-intChin)//读取正常
//继续存入
intChin <- 3 //这里报错
}
输出:
1
2
panic: send on closed channel
goroutine 1 [running]:
main.main()
14.管道的遍历
输入:
package main
import "fmt"
func main() {
var intChain chan int
intChain = make(chan int, 5)
intChain <- 1
intChain <- 2
intChain <- 3
//进行管道的遍历时,必须进行管道的关闭,否则会报错
close(intChain)
for v := range intChain {
fmt.Println("v ==> ", v)
}
}
输出:
v ==> 1
v ==> 2
v ==> 3
15.协程与管道的使用
输入:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
// 定义写入管道的函数
func write(intCh chan int) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("write ==> ", i)
intCh <- i
}
close(intCh) //防止出现死锁现象(未存储数据,先读导致)
}
// 定义读取管道的函数
func read(intCh chan int) {
defer wg.Done()
for v := range intCh {
fmt.Println("read ==> ", v)
}
}
func main() {
var intChan chan int
intChan = make(chan int, 10) // 定义无缓冲通道
//防止主线程直接结束
wg.Add(2)
//定义协程
go write(intChan)
go read(intChan)
wg.Wait()
}
输出:
write ==> 0
write ==> 1
write ==> 2
write ==> 3
write ==> 4
read ==> 0
read ==> 1
read ==> 2
read ==> 3
read ==> 4
16.只读管道与只写管道
输入:
package main
import "fmt"
func main() {
//定义只写管道类型
var inChan1 chan<- int //管道inChan1类型不变,只是增加了只读属性
inChan1 = make(chan int, 5)
inChan1 <- 1
/**
fmt.Println(<-inChan1) 爆红
*/
//定义只读管道类型
var inChan2 <-chan int
fmt.Println(inChan2)
//fmt.Println(<-inChan2) 输出报错
}
输出:
<nil>
17.select关键字
在Go语言中,select 语句是一种特殊的语句,用于同时等待多个通信操作。当多个goroutine需要同时等待多个通信操作(如多个channel的发送或接收)时,select 语句就非常有用。select 会阻塞,直到某个分支可以继续执行为止(即某个channel可以进行发送或接收操作),然后执行那个分支。如果多个channel都准备好了,select 会随机选择一个执行。
select 语句的语法类似于switch,但每个case代表一个通信操作。
输入:
package main
import (
"fmt"
"time"
)
func main() {
inChan := make(chan int, 5)
strChan := make(chan string, 5)
go func() {
time.Sleep(8 * time.Second)
inChan <- 2
}()
go func() {
time.Sleep(4 * time.Second)
strChan <- "xx"
}()
select {
case v1 := <-inChan:
fmt.Println(v1)
case v2 := <-strChan:
fmt.Println(v2)
}
}
输出:
xx
18.多协程中某一协程出现问题的处理
输入:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func print() {
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Println("i ==> ", i)
}
}
func divide() {
defer wg.Done()
defer func() {
if err := recover(); err != nil {
fmt.Println("err ==> ", err)
}
}()
var num1 = 10
var num2 = 0
fmt.Println(num1 / num2)
}
func main() {
wg.Add(2)
go print()
go divide()
wg.Wait()
}
输出:
err ==> runtime error: integer divide by zero
i ==> 0
i ==> 1
i ==> 2
i ==> 3
i ==> 4
i ==> 5
i ==> 6
i ==> 7
i ==> 8
i ==> 9