当前位置: 首页 > 面试题库 >

为什么此Go代码被阻止?

云鸿祯
2023-03-14
问题内容

我编写了以下程序:

package main

import (
    "fmt"
)

func processevents(list chan func()) {
    for {
        //a := <-list
        //a()
    }
}

func test() {
    fmt.Println("Ho!")
}

func main() {

    eventlist := make(chan func(), 100)

    go processevents(eventlist)

    for {
        eventlist <- test
        fmt.Println("Hey!")
    }
}

由于通道事件列表是一个缓冲通道,我想我应该获得100倍的输出“嘿!”,但是它只显示一次。我的错误在哪里?


问题答案:

更新(Go 1.2版或更高版本)

从Go 1.2开始,调度程序基于 抢先式多任务处理 原则。这意味着原始问题(以及下面提供的解决方案)中的问题不再相关。

从Go 1.2发行说明中

调度程序中的抢占

在以前的版本中,永远循环的goroutine可能会使同一线程上的其他goroutine饿死,当GOMAXPROCS仅提供一个用户线程时,这是一个严重的问题。在Go>
1.2中,部分解决了此问题:调度程序在输入函数时偶尔被调用。这意味着任何包含(非内联的)函数调用的循环都可以被抢占,从而允许其他goroutine在同一线程上运行。

简短答案

它不会阻止写入。它卡在的无限循环中processevents。此循环永远不会屈服于调度程序,从而导致所有goroutine无限期锁定。

如果将对的调用注释掉processevents,您将得到预期的结果,直到第100次写入为止。此时程序会出现紧急情况,因为没有人从该通道读取数据。

另一种解决方案是runtime.Gosched()在循环中调用。

长答案

使用Go1.0.2,Go的调度程序可以基于协作多任务处理原理工作。这意味着,通过在特定条件下使这些例程与调度程序交互,它将CPU时间分配给给定OS线程中运行的各种goroutine。当在goroutine中执行某些类型的代码时,就会发生这些“交互”。在go的情况下,这涉及进行某种I
/ O,系统调用或内存分配(在某些情况下)。

在空循环的情况下,永远不会遇到这种情况。因此,只要循环正在运行,就永远不允许调度程序运行其调度算法。因此,这可以防止它将CPU时间分配给其他等待运行的goroutine,从而导致观察到的结果:有效地创建了一个死锁,调度程序无法检测到该死锁。

空循环通常在Go中是不需要的,并且在大多数情况下,它将指示程序中的错误。如果出于某种原因确实需要它,则必须通过调用runtime.Gosched()每次迭代来手动让出给调度程序。

for {
    runtime.Gosched()
}

提到GOMAXPROCS将值设置> 1为解决方案。尽管这可以消除您所观察到的直接问题,但是如果调度程序决定将循环goroutine移动到自己的OS线程,则可以将问题有效地转移到其他OS线程。除非您runtime.LockOSThread()processevents函数的开头进行调用,否则无法保证。即使那样,我仍然不会依靠这种方法来解决问题。只需调用runtime.Gosched()循环本身,就可以解决所有问题,而不管goroutine在哪个OS线程中运行。



 类似资料:
  • 问题内容: var x int done := false go func() { x = f(…); done = true } while done == false { } 这是Go代码。我的恶魔告诉我,这是UB代码。为什么? 问题答案: Go Memory Model不保证该程序将始终遵守在goroutine中写入x的值。go常规销毁 部分中提供了一个类似的错误程序作为示例。 在本节中,G

  • 问题内容: 在方法或类范围内,下面的行进行编译(带有警告): 在类范围中, 变量获取其默认值 ,以下给出“未定义引用”错误: 它不是第一个应该以相同的“未定义参考”错误结束吗?还是第二行应该编译?还是我缺少什么? 问题答案: tl; dr 对于 字段 ,是非法的,因为它是对的非法前向引用。您实际上可以通过编写来解决此问题,该文件可以毫无抱怨地进行编译。 对于 局部变量 ,是非法的,因为未在使用前进

  • 问题内容: 为什么这段代码不抛出?它在不使用方法的情况下修改了一段时间,这是唯一安全的删除方法。 如果将替换为,则会得到相同的结果。但是,如果我将列表更改为或只是得到了预期的异常。到底是怎么回事?我正在使用是否相关。 编辑 我找到了以下链接 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4902078 相关部分是 天真的解决方案是将协同修改

  • 问题内容: 我不太确定这是什么意思或在做什么,有人可以详细说明吗? 问题答案: 它接受发送者引用的对象,并尝试将其转换为Player类型。Java对象是强类型的,这意味着您必须声明对象的类型。 如果发件人引用的对象不能转换为Player对象,则将为InvalidCast抛出异常。

  • 问题内容: 我一定缺少关于goroutine的一些基本知识,但是我环顾四周,我看不到任何原因无法解决这个问题。 问题答案: 您的程序在goroutines有机会运行之前已经完成。 这是带有WaitGroup的代码: https://play.golang.org/p/lmCPU9-qkB

  • 问题内容: 这是我的代码: 当运行它输出。我究竟做错了什么? 问题答案: 您正在调用一个没有隐式字符串表示形式的实际数组。 为了输出数组的内容,你可以使用,或功能或自定义输出,你可以使用,甚至是循环: