Golang协程

chan

Go语言通过提供一种轻量级的并发机制——通道(Channel),使得并发编程变得更加容易和直观。

关闭值为nil的chan

package main

// 关闭一个nil通道
func main() {
    var nilChan chan int

    // 运行时错误:panic: close of nil channel
    close(nilChan)
}

输出:panic: close of nil channel
Golang协程_第1张图片

重复关闭同一个通道

package main

// 关闭一个重复的通道
func main() {
    sameChan := make(chan int, 1)
    // 关闭通道
    close(sameChan)

    // 运行时错误:panic: close of closed channel
    // 重复关闭同一个通道
    close(sameChan)
}

输出:panic: close of closed channel
Golang协程_第2张图片

关闭只读通道

package main

// 关闭一个只读通道
func main() {

    onlyReadChan := make(<-chan int, 1)

    // 编译错误: invalid operation: cannot close receive-only channel onlyReadChan (variable of type <-chan int)
    close(onlyReadChan)
}

编译错误,输出:invalid operation: cannot close receive-only channel onlyReadChan (variable of type <-chan int)
image.png

协程使用场景

1.传递数据

通道最基本的用法就是传递数据。在Go语言中,通道是一种可以在多个Go协程之间传递数据的管道。通道可以是带缓冲的或者不带缓冲的。不带缓冲的通道是同步的,发送和接收操作必须同时准备好才能继续执行。带缓冲的通道是异步的,发送和接收操作可以独立执行,只有在缓冲区满或者空的时候才会阻塞。
下面是一个简单的例子,展示了如何使用通道传递数据:

package main

import "fmt"

func doTransfer(transfer chan<- int) {
    fmt.Println("start transfer: ")

    // 向通道写入数据
    for i := 1; i <= 5; i++ {
        fmt.Println("写入数据:", i)

        transfer <- i
    }

    // 传输完毕, 关闭
    close(transfer)
}

func main() {
    var transfer chan int
    transfer = make(chan int, 10)

    // 开始接受数据
    go doTransfer(transfer)

    // 接收数据
    for i := range transfer {
        fmt.Println("接收到数据: ", i)
    }

    fmt.Println("!!!程序退出!!!")
}

输出结果:
Golang协程_第3张图片

2.并发控制

以爬虫为例,比如需要爬取1W条数据,需要并发爬取提高效率,但是并发数有不能太大,可以通过chan来控,比如同时支持5个任务。

package main

func getUrl(url string) {

}

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

    urls := []string{"url2", "url1"}

    for _, url := range urls {
        url := url
        go func() {
            ch <- 1
            go getUrl(url)
            <-ch
        }()
    }
}

3.信号广播

通道还可以用于在多个Go协程之间广播信号。当我们需要向多个协程发送一个信号,让它们同时开始执行某个操作时,可以使用通道来实现。下面是一个例子,展示了如何使用通道进行信号广播:

package main

import (
    "fmt"
    "sync"
)

func doWorker(id int, done chan bool, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("worker %d starting\n", id)

    // 等待接收done通道的信号
    <-done

    fmt.Printf("worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    done := make(chan bool)

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go doWorker(i, done, &wg)
    }

    // 向所有协程发送信号,让它们开始执行
    close(done)

    wg.Wait()

    fmt.Println("all workers done")
}

在上面的例子中,我们创建了一个通道done用来广播信号,然后启动了五个Go协程,在协程中等待接收done通道的信号。在主函数中,我们向done通道发送了一个信号,让所有协程开始执行任务。协程在完成任务后,会向WaitGroup对象发送一个Done信号,最后我们通过调用Wait方法等待所有协程的完成信号,保证所有协程都已经执行完毕。

输出结果:
Golang协程_第4张图片

你可能感兴趣的:(go)