c语言编程笔录

首页 >   > 笔记大全

笔记大全

Go协作与抢占怎么实现

更新时间:2023-10-25

前言:

在Go语言中,协作和抢占是实现并发的两种主要方式。协作是指多个goroutine之间通过通信进行协作,以达到数据共享和同步的目的。而抢占则是指在运行时调度中,由调度器主动中断正在执行的goroutine,并将执行机会交给其他等待执行的goroutine。本文将分别介绍Go语言中如何实现协作和抢占。

实现协作:

在Go语言中,可以通过使用通道(Channel)来实现goroutine之间的协作。通道是一种用于在goroutine之间传递数据的特殊类型,通过通道,一个goroutine可以将数据发送给另一个goroutine,并等待接收响应。通道提供了同步和数据共享的机制,可以确保在发送和接收操作完成之前,发送和接收的goroutine都被阻塞。

下面是一个使用通道实现协作的示例:

package main

import "fmt"

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "processing job", j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // 启动三个goroutine进行工作
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送任务给goroutine进行处理
    for j := 1; j <= 9; j++ {
        jobs <- j
    }
    close(jobs)

    // 接收处理结果
    for a := 1; a <= 9; a++ {
        <-results
    }
}

在上述示例中,通过创建两个goroutine。一个用于发送任务,另一个用于接收处理结果。两个goroutine通过jobs和results通道进行通信。发送任务的goroutine将任务发送到jobs通道中,接收处理结果的goroutine从results通道中接收处理结果。

通过通道的使用,这两个goroutine在接收到任务或者处理结果之前都会被阻塞,从而实现了协作。

实现抢占:

Go语言调度器采用了一种称为抢先调度的策略,它会在某个goroutine阻塞或结束时,主动让出其执行权,并将执行机会交给等待执行的其他goroutine。这种抢占式调度机制确保了每个goroutine能够平等地获得执行的机会,并防止某个goroutine长时间占用CPU。

Go语言并发的抢占式调度机制是通过每个goroutine的调度点来实现的。当一个goroutine发生调度点时,调度器能够决定是否要抢占该goroutine。在Go语言中,调度点主要包括:

  • 通道操作:发送和接收通道数据时,会发生调度点。
  • 系统调用:当调用一些涉及底层操作系统的函数时,可能会产生调度点。
  • 运行时间片结束:当一个goroutine运行的时间片用完时,会发生调度点。
  • 主动调用runtime.Gosched()函数:该函数会主动让出当前goroutine的执行权。

下面是一个示例,演示在Go语言中如何实现抢占:

package main

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

func main() {
    // 设置并发执行的最大CPU数目
    runtime.GOMAXPROCS(1)

    // 定义一个计数器
    counter := 0

    // 定义一个无限循环
    go func() {
        for {
            counter++
            time.Sleep(time.Millisecond)
        }
    }()

    // 定义一个死循环
    for {
        // 每隔一段时间输出计数器的值
        fmt.Println("Counter:", counter)
        time.Sleep(time.Millisecond * 100)
    }
}

在上述示例中,我们启动了两个goroutine。一个是一个无限循环,在每次循环中将计数器加1,并休眠1毫秒;另一个是一个死循环,每隔100毫秒输出一次计数器的值。由于我们将最大CPU数目设置为1,所以两个goroutine会交替获得执行的机会。

通过定时的休眠,无限循环的goroutine会将CPU使用权让给死循环的goroutine,从而实现了抢占。

总结:

通过使用通道实现协作和利用抢占机制可以实现在Go语言中的并发编程。

协作通过使用通道实现,可以通过通信来实现goroutine之间的数据共享和同步。通道提供了同步和阻塞的机制,保证了数据的安全性。

抢占则是指在运行时调度中,由调度器主动中断正在执行的goroutine,并将执行机会交给其他等待执行的goroutine。Go语言的调度器通过调度点来实现抢占,确保每个goroutine能够平等地获得执行的机会。

协作和抢占是Go语言中实现并发的两种重要方式,开发者可以根据具体需求选择适合的方式来实现并发编程。