golang怎么并发
更新时间:2023-09-09引言
并发是Go语言的本质特性之一,被广泛用于处理并行和分布式系统中。Go语言使用协程(或者叫Goroutine)和通道(channel)来实现并发。本文将以这两种方式为主,介绍golang中如何实现并发。
协程(Goroutine)
Goroutine是Golang提供的一种轻量级线程,它和操作系统线程不同,它是由Go语言的运行时负责调度的。在编写代码时,我们只需要定义一个函数并在函数调用前加上"go"关键字,即可创建一个新的协程。
func worker(i int) { fmt.Printf("Worker %d starting\n", i) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", i) } func main() { for i := 0; i < 5; i++ { go worker(i) } time.Sleep(time.Second * 2) fmt.Println("Main function exiting...") }
在上面的例子中,我们定义了一个"worker"函数,它会输出一些信息,休眠1秒钟,然后再输出另一些信息。在"main"函数中,我们使用一个循环语句创建了5个协程,每个协程都会调用"worker"函数。最后等待2秒钟,使程序有足够的时间执行完所有的协程,并打印"Main function exiting..."。
通道(channel)
通道是Golang提供的一种用于并发程序之间通信的方式。通道在多协程之间传递数据,保证了并发竞争的安全性。操作通道的关键字是"chan",用于定义通道的数据类型。使用通道时,需要注意的一点是:通道发送操作和接收操作是阻塞的。只有在有对应的接收方或发送方时,通道才会释放它们。我们可以使用"select"语句来避免通道阻塞,与接收方同步进行。
func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("Worker %d started job %d\n", id, j) time.Sleep(time.Second) fmt.Printf("Worker %d finished job %d\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 100) results := make(chan int, 100) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 9; j++ { jobs <- j } close(jobs) for a := 1; a <= 9; a++ { <-results } }
在上面的例子中,我们定义了一个"worker"函数,它会从通道"jobs"中获取一些工作,然后处理这些工作,将处理结果发送到通道"results"中。"main"函数则创建了3个协程并在循环内向通道"jobs"中发送了9个工作任务。我们注意到在最后一个循环中执行了9次"<-results"操作,这个操作会立即阻塞直到有结果被发送到通道"results"中。这样可以确保所有的工作都被正确地处理,并且每个工作都有一个对应的结果。
并发安全与锁
并发安全是指在多线程或多协程之间共享资源时,保证资源被访问时的正确性和同步。在Go语言中,可以使用sync包中的互斥锁实现对共享资源的保护。互斥锁是一种并发控制机制,它保证同一时间只有一个协程可以访问共享资源。
var mutex sync.Mutex var balance int func Deposit(amount int) { mutex.Lock() balance += amount mutex.Unlock() } func Balance() int { mutex.Lock() b := balance mutex.Unlock() return b }
在上面的例子中,我们定义了一个互斥锁"mutex"和一个变量"balance",用于保护对"balance"变量的读写。在"Deposit"和"Balance"函数中,我们使用了"mutex.Lock"和"mutex.Unlock"方法来保护对"balance"变量的读写,从而确保多个协程访问"balance"变量时的并发安全性。