并发Goroutine

本文介绍了Golang中的并发编程基础,包括goroutine的创建和使用,以及channel作为goroutine间通信和同步的机制。goroutine是轻量级线程,通过go关键字启动,channel则提供了安全的数据传递。示例展示了如何使用channel进行数据传输和goroutine间的并发计算,以及select语句在处理多个通道并发操作中的应用。
摘要由CSDN通过智能技术生成

Golang是一门天生支持并发的语言,它提供了一些原生的机制和关键字,使得开发者可以轻松地编写并发程序。在本教程中,我们将详细介绍Golang中的并发相关概念和实现方式。

goroutine和channel是Golang并发的双驱动,goroutine可以实现多个协程的快速创建,channel可以在并发中安全使用,所以channel可以安全的在goroutine中传递数据

Goroutine

Goroutine是Golang并发编程的基本单元,类似于线程,但更轻量级、更高效、更易于使用。Goroutine可以在一个进程内并发执行,而不会受到操作系统线程调度器的限制。Golang的并发模型采用了CSP(Communicating Sequential Processes)模型,即通过通信来共享内存,而不是通过共享内存来通信。

Goroutine的创建非常简单,只需在函数调用前添加关键字go即可。例如:

func main() {
    go func() {
        // 这里是一个新的goroutine
    }()
    // 主goroutine继续执行其他任务
}

在上面的示例中,我们使用了匿名函数创建了一个新的goroutine,并在主goroutine中继续执行其他任务。

Goroutine是轻量级的,创建和销毁的开销很小,因此可以轻松地创建大量的goroutine来处理并发任务。

Channel

Golang中的channel是一种用于在goroutine之间进行通信的机制。Channel可以用于传递数据,也可以用于同步goroutine之间的执行。

创建一个channel的方式是使用内置函数make,指定其缓冲区大小(可选)。例如:

ch := make(chanint) // 无缓冲channel
ch2 := make(chanstring, 10) // 有缓冲channel,缓冲区大小为10

无缓冲channel的特点是必须要有发送者和接收者同时准备好,才能进行数据传输,否则发送者和接收者都会阻塞。而有缓冲channel则可以在缓冲区未满的情况下进行发送操作,直到缓冲区满了才会阻塞发送者。

以下是一个使用channel进行数据传输的示例:

func main() {
    ch := make(chanint)
    gofunc() {
        ch <- 1// 发送数据
    }()
    data := <- ch // 接收数据
    fmt.Println(data) // 输出1
}

在上面的示例中,我们使用channel进行数据传输,首先在一个新的goroutine中发送数据1,然后在主goroutine中接收数据并输出

下面的例子展示了如何使用goroutine和channel实现并发计算:

func calculate(data []int, ch chanint) {
    sum := 0
    for _, value := range data {
        sum += value
    }
    ch <- sum
}

func main() {
    data := []int{1, 2, 3, 4, 5}

    // 创建一个整数类型的channel
    ch := make(chanint)

    // 启动两个goroutine并发计算go calculate(data[:len(data)/2], ch)
    go calculate(data[len(data)/2:], ch)

    // 从channel中接收计算结果
    result1, result2 := <-ch, <-ch

    // 输出计算结果
    fmt.Println(result1 + result2)
}

在上面的例子中,我们使用两个goroutine并发计算data切片中元素的和,并将计算结果发送到同一个channel中。最后,我们从该channel中接收计算结果,将它们相加并输出。

select

在Golang中,select语句用于处理多个通道的并发操作。它可以等待多个通道中的任何一个通道操作完成并执行相应的操作。在这个教程中,我们将详细介绍select语句的使用和注意事项。

select语句的语法

下面是select语句的一般语法:

select {
case <-ch1:
    // 执行 ch1 的操作case x := <-ch2:
    // 执行 ch2 的操作,并将接收到的值赋给 xcase ch3 <- y:
    // 执行 ch3 的操作,并将 y 发送到 ch3 中default:
    // 没有任何一个通道准备就绪,执行 default 操作
}

在select语句中,我们可以通过case分支来处理通道的发送和接收操作。通常,每个case分支都是一次通道操作,这些操作可能会阻塞当前的goroutine,直到通道可以被操作。

在上面的语法中,箭头符号(<-)表示通道操作的方向。通道的接收操作使用箭头符号指向通道,而通道的发送操作使用箭头符号指向值。在接收操作中,通常使用变量来存储接收到的值。在发送操作中,我们可以使用任何值来发送到通道中。

除了case分支外,select语句还有一个default分支,该分支在所有通道都不可用时执行。如果default分支不存在,则select语句会一直阻塞,直到有至少一个通道可用为止。

select语句的使用示例

下面是一个使用select语句的示例,其中有两个通道ch1和ch2,它们被用于并发接收和发送操作:

func main() {
    ch1 := make(chanint)
    ch2 := make(chanint)

    // 向通道 ch1 中发送值
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- 1
    }()

    // 向通道 ch2 中发送值
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- 2
    }()

    // 使用 select 等待通道操作完成select {
    case x := <-ch1:
        fmt.Println("接收到 ch1 中的值:", x)
    case y := <-ch2:
        fmt.Println("接收到 ch2 中的值:", y)
    }
}

在上面的示例中,我们首先创建了两个通道ch1和ch2。接下来,我们使用两个goroutine分别向这两个通道发送值,这些goroutine使用了time.Sleep函数来模拟工作。最后,我们使用select语句等待ch1和ch2中的任何一个通道操作完成。一旦其中一个通道的操作完成,select语句将立即执行相应的case分支,并输出接收到的值。

select语句的注意事项

  1. select 语句中的每个 case 必须是一个通信操作,即发送或接收操作。

  1. select 语句会随机执行一个可运行的 case,如果有多个可运行的 case,则会随机选择一个执行。

  1. 如果 select 语句中的所有 case 都阻塞了,且存在 default 语句,则会执行 default 语句。

  1. 如果 select 语句中的所有 case 都阻塞了,且不存在 default 语句,则会一直阻塞,直到有一个 case 可以运行。

  1. 如果 select 语句中的某个 case 可以运行,那么只会执行该 case,而不会执行其他 case

  1. 如果 select 语句中的某个 case 是一个接收操作,那么在该 case 中接收到的值是该通道的元素值,而不是该通道的引用。

  1. 如果 select 语句中的某个 case 是一个发送操作,那么在该 case 中发送的值必须是可比较的类型,否则会导致编译错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值