ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### sync包 go语言多线程,子线程运行时间大于主线程时,主线程运行完毕就会关闭程序,届时子线程没运行完。 示例代码:创建一个主线程和一个test的子线程,主线程和子线程都使用for循环输出i,子线程时间间隔为3秒,主线程为1秒。子线程执行时间大于主线程 ``` package main import ( "fmt" "time" ) // 子进程函数 func test() { for i := 0; i < 10; i++ { fmt.Println("子线程输出", i) // 这里加一个子线程堵塞时间间隔 3秒 比主线程少1秒 time.Sleep(time.Second * 3) } } // main函数的是主线程 func main() { // 如果不加关键词 go 就会顺序执行 先执行 test函数的 // 加关键词 go 但是test函数没有执行, 是因为主线程运行太快直接结束关闭了程序 go test() for i := 0; i < 10; i++ { fmt.Println("主线程输出", i) // 这里个主线程加一个时间堵塞,但是时间比子线程少 time.Sleep(time.Second) } // 所以 需要加 一个休眠时间堵塞一下 time.Sleep(time.Second) fmt.Println("主线程结束") } //总结: // go语言多线程 需要加时间堵塞 才能运行 // 子线程运行时间大于主线程时,主线程运行完毕就会关闭程序,届时子线程没运行完 ``` 基于上面的示例代码,使用sync包里面**sync.WaitGroup** (等待组) 示例代码:和上面一样,这里使用sync包里面的等待组sync.WaitGroup 第一步创建全局变量,wg类型sync.WaitGroup 第二步使用Add()函数协程计数器加1 第三步,子进程结束使用Done()函数协程计数器减1 第四步主进程使用Wait()函数等待子进程 ``` package main import ( "fmt" "sync" "time" ) // 第一步 创建一个全局变量 wg 类型是sync.WaitGroup var wg sync.WaitGroup // 子进程函数 func test() { for i := 0; i < 10; i++ { fmt.Println("子线程输出", i) time.Sleep(time.Second * 2) } // 第三步,子进程结束 使用sync包里面的 Done()函数 协程计数器加-1 wg.Done() } // main函数的是主线程 func main() { // 第二步,调用协程时,使用sync包里面的 Add()函数 协程计数器加1 wg.Add(1) go test() for i := 0; i < 10; i++ { fmt.Println("主线程输出", i) time.Sleep(time.Second) } // 第四步,调用协程时,使用sync包里面的 Wait()函数 主线程等待子线程结束 wg.Wait() fmt.Println("主线程结束") } // 总结:这样就会一直等到子线程直线完毕 程序才关闭 ``` ``` package main import ( "fmt" "sync" ) func main() { wg := sync.WaitGroup{} wg.Add(100) for i := 0; i < 100; i++ { go func(i int) { fmt.Println(i) wg.Done() }(i) } wg.Wait() } ``` 多个协程示例代码 ``` package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup // 子进程函数 func test(num int) { for i := 0; i < 10; i++ { fmt.Println("线程", num, "输出", i) // time.Sleep(time.Second * 2) 堵塞2秒 time.Sleep(time.Millisecond * 100) // 100毫秒 } wg.Done() } // main函数的是主线程 func main() { for i := 0; i < 3; i++ { wg.Add(1) go test(i) } for i := 0; i < 10; i++ { fmt.Println("主线程输出", i) time.Sleep(time.Millisecond * 20) // 20毫秒 } wg.Wait() fmt.Println("主线程结束") } ``` 互斥锁 **sync.Mutex** 加互斥锁主要解决(编译后)资源竞争的问题。比如多个线程抢占一个资源,例如多个协程读取数据库里面的一条内容。加了锁之后,只有一个协程读取,其他的等待它读取完再执行操作。 示例代码:创建变量j初始值为0,test函数执行一次,j就会递增。如果多个进程同时使j递增的话,就可能因为进程抢占资源报错。但是加了互斥锁之后,就会等一个协程执行完之后,再执行另外一个。就不会发生资源抢占而报错。 ``` package main import ( "fmt" "sync" ) // 逻辑中使用的某个变量 var j = 0 // 与变量对应的使用互斥锁 var mutex sync.Mutex // 等待组 var wg sync.WaitGroup // 子进程 func test() { // 逻辑开始加互斥锁 mutex.Lock() // 逻辑遍历递增 j++ fmt.Println("逻辑变量", j) // 去逻辑锁 mutex.Unlock() // 协程计数器减1 wg.Done() } //主进程 func main() { for i := 0; i < 10; i++ { // 协程计数器加1 wg.Add(1) go test() } // 等待子进程结束关闭程序 wg.Wait() } ``` 读写互斥锁 **sync.RWMutex** 读写互斥锁定,可以让多个读操作并发,同时读取,但是对于写操作是完全互斥的。也就是说当一个协程进行写操作的时候,其他协程不能进行读操作也不也能写操作。 示例代码:10个进程执行写的操作,10个进程执行读的操作,加上读写互斥锁之后,写的操作是互斥的,只能一个进程操作完,下个进程才能操作。读的时候是并行的。 ``` package main import ( "fmt" "sync" "time" ) // 等待组 var wg sync.WaitGroup // 读写互斥锁 var mutex sync.RWMutex // 写操作的协程 func write() { // 加互斥锁 mutex.Lock() fmt.Println("@@@执行写操作") time.Sleep(time.Millisecond * 1000) //1000毫秒 // 去锁 mutex.Unlock() wg.Done() } func read() { // 加读写互斥锁 mutex.RLock() fmt.Println("执行读操作$$$") time.Sleep(time.Millisecond * 1000) //1000毫秒 // 去锁 mutex.RUnlock() wg.Done() } func main() { // 10个协程执行写的操作 for i := 0; i < 10; i++ { wg.Add(1) go write() } // 10协程执行读的操作 for i := 0; i < 10; i++ { wg.Add(1) go read() } wg.Wait() } ```