day13 Goroutine 协程

了解计算机原理

  • 进程:计算机资源分配单位
  • 线程:cpu处理单位
  • 协程:以 特殊机制或者函数实现高并发,又称 轻量级线程

了解Goroutine

  • Go Goroutine, go语言中的协程,实现并发。
  • 关键字 go
  • 初始大小 4k,随着程序执行自动增长和删除
  • 实现多线程 并发 执行
package main

import "fmt"

func helloGoroutine() {
	for i := 0; i < 100; i++ {
		fmt.Println("hello world", i)
	}
}

func main() {
	/*
		Go Goroutine, go语言中的协程,实现并发。
		- 关键字 go
		- 初始大小 4k,随着程序执行自动增长和删除
		- 实现多线程 并发 执行
	*/

	// 使用 go 实现协程
	go helloGoroutine()
	for i := 0; i < 100; i++ {
		fmt.Println("main ", i)
	}
}

runtime包

  • 获取系统的信息的工具包
package main

import (
	"fmt"
	"runtime"
	"time"
)

func goSchedFunc() {
	go func() {
		for i := 0; i < 6; i++ {
			fmt.Println("goroutine", i)
		}
	}()
	runtime.Gosched()
	for i := 0; i < 6; i++ {
		fmt.Println("Main goroutine", i)
	}
}

func runtimeGoExitFunc() {
	// 开启一个 goroutine 线程(协程,轻量级线程)
	go func() {
		fmt.Println("Start runtimeGoExitFunc")
		test()
		fmt.Println("End runtimeGoExitFunc")
	}()
	time.Sleep(time.Second * 3) // 当前函数睡三秒

}

func test() {
	defer fmt.Println("延时关闭test函数")
	runtime.Goexit() // 终止程序,即:结束当前线程(goroutine)
	fmt.Println("执行test函数")
}

func main() {
	/*
		runtime 包
		- 获取系统的信息的工具包
	*/

	// 获取 goRoot 目录
	fmt.Println("GoRoot Path", runtime.GOROOT())

	// 获取操作系统
	fmt.Println("System", runtime.GOOS) // darwin

	// 获取CPU数量
	fmt.Println("NumCPU", runtime.NumCPU()) // 12

	// runtime.Gosched() 线程的礼让。让出时间片,即:让goroutine先执行
	goSchedFunc()

	// NumGoroutine返回当前存在的运行例程的数量。
	fmt.Println("NumGoroutine", runtime.NumGoroutine())

	// runtime.Goexit() 比肩 panic 终止程序
	runtimeGoExitFunc()
	//
}

多线程问题

  • 临界资源安全问题。多线程调用共享变量
  • 案例:售票问题
package main

import (
	"fmt"
	"time"
)

func multithreadedSharedVariableMethods() {
	shareVariable := 1
	go func() {
		shareVariable = 2
		fmt.Println("GoRoutine shareVariable:", shareVariable)
	}()
	shareVariable = 3
	time.Sleep(1 * time.Second)
	fmt.Println("main shareVariable:", shareVariable)

}
func main() {
	/*
		go 中 多线程的问题
			- 临界资源安全问题。多线程调用共享变量
			- 案例:售票问题
				-
	*/

	// 案例1。多线程共享变量
	multithreadedSharedVariableMethods()

}

sync 同步锁

  • 锁机制处理多线程抢占资源的问题
    • 某个时间段内,只允许一个 goroutine访问共享数据。当goroutine访问完毕,释放锁后,其他goroutine才能访问
    • 不要以共享内存的方式去通信,而是以通信方式去共享内存
    • 不推荐使用 锁机制。 go语言中! 其他语言基本上都是以锁的方式处理
    • go 中鼓励通过 channel 来解决 共享问题
package main

import (
	"fmt"
	"sync"
	"time"
)

// SmLock 同步锁【互斥锁】
var SmLock sync.Mutex

// Swg 同步等待组。处理子协程执行完成
var Swg sync.WaitGroup

// TickerNums 定义全局 票的数量
var TickerNums = 10

func saleTicketFunc(ticketSeller string) {
	// 卖票函数
	for {
		if TickerNums > 0 {
			fmt.Println("获取票总量:", TickerNums)
			TickerNums--
			fmt.Println("售票员:", ticketSeller, "卖出1张票", "剩余票的量:", TickerNums)

		} else {
			fmt.Println("票卖完了!")
			break
		}
	}
}

func saleTicketMainFunc() {
	// 卖票主函数
	go saleTicketFunc("张三")
	go saleTicketFunc("李四")
	go saleTicketFunc("王五")
	go saleTicketFunc("赵六")
	time.Sleep(3 * time.Second)
}

func saleTicketMutexWaitGroupFunc(ticketSeller string) {
	// 卖票函数
	for {
		// 增加锁机制,锁住共享资源
		SmLock.Lock() // 上锁
		if TickerNums > 0 {
			fmt.Println("获取票总量:", TickerNums)
			TickerNums--
			fmt.Println("售票员:", ticketSeller, "卖出1张票", "剩余票的量:", TickerNums)

		} else {
			SmLock.Unlock() // 释放锁,否则进入锁死。
			fmt.Println("票卖完了!")
			break
		}
		SmLock.Unlock() // 释放锁
	}
}
func saleTicketMutexWaitGroupMainFunc() {
	// 增加售票互斥锁,增加上同步等待组。 卖票主函数
	Swg.Add(4)
	go saleTicketMutexFunc("张三")
	go saleTicketMutexFunc("李四")
	go saleTicketMutexFunc("王五")
	go saleTicketMutexFunc("赵六")
	Swg.Wait() // 等待全部协程处理完问题
}

func saleTicketMutexFunc(ticketSeller string) {
	// defer 延时关闭 同步等待组
	defer Swg.Done()
	// 卖票函数
	for {
		// 增加锁机制,锁住共享资源
		SmLock.Lock() // 上锁
		if TickerNums > 0 {
			fmt.Println("获取票总量:", TickerNums)
			TickerNums--
			fmt.Println("售票员:", ticketSeller, "卖出1张票", "剩余票的量:", TickerNums)

		} else {
			SmLock.Unlock() // 释放锁,否则进入锁死。
			fmt.Println("票卖完了!")
			break
		}
		SmLock.Unlock() // 释放锁
	}
}
func saleTicketMutexMainFunc() {
	// 增加售票互斥锁。 卖票主函数
	go saleTicketMutexFunc("张三")
	go saleTicketMutexFunc("李四")
	go saleTicketMutexFunc("王五")
	go saleTicketMutexFunc("赵六")
	time.Sleep(3 * time.Second)
}

func main() {
	/*
		sync 包。 锁机制处理多线程抢占资源的问题
			- 某个时间段内,只允许一个 goroutine访问共享数据。当goroutine访问完毕,释放锁后,其他goroutine才能访问
			- 不要以共享内存的方式去通信,而是以通信方式去共享内存
			- 不推荐使用 锁机制。 go语言中! 其他语言基本上都是以锁的方式处理
			- go 中鼓励通过 channel 来解决 共享问题
			-
	*/

	// 普通卖票。问题:共享资源抢占,sleep处理协程等待执行完成
	saleTicketMainFunc()

	// 进阶卖票。处理:解决共享资源抢占问题。 问题:sleep处理协程等待执行完成
	saleTicketMutexMainFunc()

	// 高阶卖票。处理:解决共享资源抢占问题,解决协程等待执行完成问题
	saleTicketMutexWaitGroupMainFunc()

}

sync.WaitGroup 同步等待组

package main

import (
	"fmt"
	"runtime"
	"sync"
)

// SwgTest 定义 同步等待组
var SwgTest sync.WaitGroup

func test1() {
	defer SwgTest.Done()
	for i := 0; i < 10; i++ {
		fmt.Println("T1", i)
	}
}
func test2() {
	for i := 0; i < 10; i++ {
		fmt.Println("T2", i)
	}
	SwgTest.Done()
}

func main() {
	// 同步等待组。处理 goroutine 的等待问题

	SwgTest.Add(2) // 设定 线程 数量
	go test1()
	runtime.Gosched() // 下面的线程让出资源!
	go test2()

	// 垃圾处理: time.Sleep(time.Second * 3)
	//time.Sleep(time.Second * 1)

	// 优秀的处理: 同步等待组等待执行完成
	SwgTest.Wait()

}

sync 的 锁 Lock + WaitGroup 同步等待组

package main

import (
	"fmt"
	"sync"
)

// TickerNums2 定义全局 票的数量
var TickerNums2 = 10

// SmLock2 同步锁【互斥锁】
var SmLock2 sync.Mutex

// Swg2 同步等待组。处理子协程执行完成
var Swg2 sync.WaitGroup

func saleTicketMutexFunc2(ticketSeller string) {
	// defer 延时关闭 同步等待组
	defer Swg2.Done()
	// 卖票函数
	for {
		// 增加锁机制,锁住共享资源
		SmLock2.Lock() // 上锁
		if TickerNums2 > 0 {
			fmt.Println("获取票总量:", TickerNums2)
			TickerNums2--
			fmt.Println("售票员:", ticketSeller, "卖出1张票", "剩余票的量:", TickerNums2)

		} else {
			SmLock2.Unlock() // 释放锁,否则进入锁死。
			fmt.Println("票卖完了!")
			break
		}
		SmLock2.Unlock() // 释放锁
	}
}
func saleTicketMutexWaitGroupMainFunc2() {
	// 增加售票互斥锁,增加上同步等待组。 卖票主函数
	Swg2.Add(4)
	go saleTicketMutexFunc2("张三")
	go saleTicketMutexFunc2("李四")
	go saleTicketMutexFunc2("王五")
	go saleTicketMutexFunc2("赵六")
	Swg2.Wait() // 等待全部协程处理完问题
}
func main() {

	// 高阶卖票。处理:解决共享资源抢占问题,解决协程等待执行完成问题
	saleTicketMutexWaitGroupMainFunc2()
}

posted @ 2024-07-02 08:47  染指未来  阅读(1)  评论(0编辑  收藏  举报