Golang中的goroutine并发编程

Golang中的goroutine并发编程

Golang中的goroutine并发编程

什么是goroutine

在Golang中,goroutine是一种轻量级的线程实现,可以在一个程序中同时执行多个任务。Goroutine由Go语言的运行时系统调度,并且可以在多核处理器上并行执行。与传统的线程相比,goroutine的创建和销毁开销更小,因此可以高效地支持大量的并发任务。

在Golang中,使用go关键字可以启动一个新的goroutine。例如:

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello, goroutine!")
}

func main() {
    go sayHello()
    time.Sleep(1 * time.Second) // 等待goroutine执行完毕
    fmt.Println("Main goroutine exiting.")
}

上面的代码中,sayHello()函数会在一个新的goroutine中执行,而main函数则在主goroutine中执行。使用go关键字启动goroutine后,程序会继续往下执行,不会等待goroutine执行完毕。

goroutine的调度

在Golang中,goroutine的调度由运行时系统自动管理,开发者不需要关心具体的调度细节。Golang的运行时系统使用了类似于工作窃取(work stealing)的调度算法,能够高效地利用多核处理器。

在Golang中,可以通过runtime.GOMAXPROCS(n)函数设置并发执行的最大CPU核数。默认情况下,Golang会根据当前计算机的CPU核数来设置并发执行的最大核数。

package main

import (
    "fmt"
    "runtime"
)

func main() {
    numCPU := runtime.NumCPU()
    fmt.Println("CPU核数:", numCPU)

    // 设置并发执行的最大CPU核数为1
    runtime.GOMAXPROCS(1)

    numCPU = runtime.GOMAXPROCS(0)
    fmt.Println("最大CPU核数:", numCPU)
}

上面的代码中,runtime.NumCPU()函数可以获取当前计算机的CPU核数,runtime.GOMAXPROCS()函数可以设置并发执行的最大CPU核数。

goroutine间的通信

在Golang中,goroutine之间的通信是通过channel完成的。Channel是一种类型,可以用来在goroutine之间传递数据。Channel可以被用来同步goroutine的执行顺序,也可以被用来传递数据。

package main

import (
    "fmt"
)

func produce(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch)
}

func consume(ch chan int) {
    for {
        val, ok := <-ch
        if !ok {
            break
        }
        fmt.Println("Consumed:", val)
    }
}

func main() {
    ch := make(chan int)

    go produce(ch)
    go consume(ch)

    fmt.Scanln() // 防止主goroutine提前退出
}

上面的代码中,produce函数向channel发送数据,consume函数从channel接收数据。在main函数中,创建了一个channel,并分别在两个goroutine中调用produceconsume函数。

goroutine的同步

在多个goroutine之间进行数据交互时,必须保证数据的一致性。Golang提供了多种同步机制,如sync.WaitGroupsync.Mutex等,来保证goroutine之间的同步。

package main

import (
    "fmt"
    "sync"
)

var (
    count int
    lock  sync.Mutex
    wg    sync.WaitGroup
)

func increment() {
    defer wg.Done()

    lock.Lock()
    count++
    lock.Unlock()
}

func main() {
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go increment()
    }

    wg.Wait()
    fmt.Println("Count:", count)
}

上面的代码中,使用sync.Mutex来保护共享变量count的访问。通过sync.WaitGroup来等待所有goroutine执行完毕,保证主goroutine在所有goroutine执行完毕后再退出。

goroutine的异常处理

在Golang中,goroutine中的异常无法被外部捕获,因此需要在goroutine内部进行异常处理。可以使用recover函数来捕获goroutine中的异常,并在defer函数中进行处理。

package main

import (
    "fmt"
)

func div(a, b int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
        }
    }()

    if b == 0 {
        panic("division by zero")
    }

    fmt.Println("Result:", a/b)
}

func main() {
    go div(10, 0)

    fmt.Scanln() // 防止主goroutine提前退出
}

上面的代码中,div函数在除法运算时可能会引发异常,使用recover函数来捕获异常并处理。在异常发生时,程序会打印出异常信息并继续执行。

总结

通过本文的介绍,我们了解了Golang中goroutine的基本概念和用法。Goroutine是Golang并发编程的重要组成部分,可以高效地支持大规模并发任务。在实际开发中,合理地使用goroutine可以提高程序的性能和响应速度,同时注意处理好goroutine间的通信和同步,确保程序的正确性和稳定性。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程