【Golang 面试题】每日 3 题(五十五)

✍个人博客:Pandaconda-优快云博客
📣专栏地址:http://t.csdnimg.cn/UWz06
📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

163. Go hand off 整个分离流程是什么?

当前线程 M 阻塞时,释放 P,给其它空闲的 M 处理。

在这里插入图片描述

164. GMP 中 hand off 机制又是什么?

GMP 中的 hand off 机制是指在某个 M 线程需要将当前正在执行的 Goroutine 交给另一个 M 线程时,使用的一种机制。

具体地,hand off 机制的实现过程如下:

当一个 M 线程需要将当前正在执行的 Goroutine 交给另一个 M 线程时,它会将该 Goroutine 和一个指向目标 M 线程的指针打包成一个结构体,称为 hand off 对象。
当目标 M 线程的本地队列中没有 Goroutine 可供执行时,它会从全局队列中获取一个 hand off 对象,并尝试将其中的 Goroutine 从原来的 M 线程中获取出来,添加到自己的本地队列中执行。在此期间,当前 M 线程会不断尝试从全局队列中获取 Goroutine 并将其调度到本地队列中执行。

当目标 M 线程成功获取到 hand off 对象后,它会将其中的 Goroutine 添加到自己的本地队列中,并将它们调度到绑定的 P 上执行。

hand off 机制的好处是可以避免线程饥饿,提高 Goroutine 的调度效率。当一个 M 线程需要将当前正在执行的 Goroutine 交给另一个 M 线程时,可以使用 hand off 机制来尽快地将 Goroutine 交给目标 M 线程,从而避免线程饥饿的问题。同时,由于 hand off 机制只在需要将当前正在执行的 Goroutine 交给另一个 M 线程时才会被使用,因此相对于 work stealing 机制来说,它的实现比较简单,不会增加太多额外的开销。

165. 如何实现 GMP 中的 hand off 机制?

由于 hand off 机制的使用场景比较特殊,且需要涉及到多个 Goroutine 之间的交互,因此比较难以直接演示。

不过,我们可以通过一个简单的示例来说明 hand off 机制的基本使用方法和效果。
假设我们有一个生产者-消费者模型,其中有多个生产者 Goroutine 和多个消费者 Goroutine,它们都需要不断地从一个共享的队列中获取任务进行处理。为了提高并发效率,我们可以使用 GMP 模型来对任务进行调度。

在这个示例中,我们使用一个全局队列来存储任务,并使用 hand off 机制来将任务从一个 M 线程转移到另一个 M 线程。每个生产者 Goroutine 和消费者 Goroutine 都会不断地尝试从全局队列中获取任务,并将其添加到自己的本地队列中执行。当某个 Goroutine 的本地队列为空时,它会从全局队列中获取一个 hand off 对象,并将其中的 Goroutine 从原来的 M 线程中获取出来,添加到自己的本地队列中执行。在此期间,其他 Goroutine 也可以从全局队列中获取任务,并将其添加到自己的本地队列中执行。

示例代码如下:

package main
import (
        "fmt"
        "sync"
        "time"
)
// 全局变量,用于保存正在处理的任务
var currentTask int
func producer(tasks chan<- int, wg *sync.WaitGroup) {
        defer wg.Done()
        // 生产 10 个任务
        for i := 1; i <= 10; i++ {
                fmt.Printf("producer producing task %d\n", i)
                tasks <- i
                time.Sleep(time.Second)
        }
        // 关闭任务通道
        close(tasks)
}
func consumer(id int, tasks <-chan int, done chan<- bool, wg *sync.WaitGroup) {
        defer wg.Done()
        for task := range tasks {
                fmt.Printf("consumer %d processing task %d\n", id, task)
                // 模拟处理任务的耗时
                time.Sleep(time.Second)
                // 交出任务,使用 hand off 机制
                currentTask = task
                done <- true
        }
        fmt.Printf("consumer %d has processed all tasks\n", id)
}
func main() {
        var wg sync.WaitGroup
        // 任务通道
        tasks := make(chan int)
        // done 通道,用于实现 hand off 机制
        done := make(chan bool)
        // 启动 3 个 consumer goroutine
        for i := 1; i <= 3; i++ {
                wg.Add(1)
                go consumer(i, tasks, done, &wg)
        }
        // 启动 producer goroutine
        wg.Add(1)
        go producer(tasks, &wg)
        // 等待所有 goroutine 执行完毕
        wg.Wait()
        // 所有任务处理完毕后,输出最后一个交出任务的 consumer ID 和任务 ID
        fmt.Printf("last consumer to hand off task: %d, task ID: %d\n", currentTask%3+1, currentTask)
}

在这个示例中,我们定义了一个全局变量 currentTask,用于保存当前正在处理的任务。在 consumer goroutine 中,当处理完一个任务后,使用 hand off 机制将任务交出,并更新 currentTask 的值。在程序结束时,我们可以通过输出 currentTask 的值来查看最后一个交出任务的 consumer ID 和任务 ID。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值