RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1548396
Accepted
Quester
Quester
Asked:2023-10-28 19:20:05 +0000 UTC2023-10-28 19:20:05 +0000 UTC 2023-10-28 19:20:05 +0000 UTC

为什么嵌套 goroutine 的结果不显示以及如何修复?

  • 772

我很难理解 goroutine 和通道。结果这个话题对我来说很难,有一瞬间困扰着我(因为我不明白)。我将提供完整的代码,因为我不确定问题是否出在 somework 函数中(尽管几乎肯定存在)。

这个问题完全是虚构的,要理解:

当其中一个嵌套 goroutine 正在执行长时间运行的操作时,如何继续执行所有 goroutine,包括 main...

我把我所有的想法写在代码的注释中,包括标记出对我提出问题的地方。因此,欢迎直接上代码。感谢您的关注和时间。

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "time"
)

func main() {
    start := time.Now().UnixNano()
    in, out, wait := make(chan int), make(chan int), make(chan bool)
    go somework(in, out, wait, 12)
    // записываем рандомные данные в канал который потом
    // передадим в качестве аргумена в somework
    in <- int(time.Now().Unix())
    // выводим результат работы somework
    fmt.Printf("somework вернула в канале значение: %v\n", <-out)
    end := time.Now().UnixNano()
    fmt.Printf("функция main завершилась через >>%v<< наносекунд после своего начала, выполнив все второстепенные горутины\n", end-start)
    // ждем, пока somework просигнализирует об окончании своей работы.
    <-wait
}

// некая функция, которая внутри себя должна отправить на выполнение длительную операцию.
// все горутины, включая main должны продолжать свое выполнение и когда завершится длительная
// операция вывести результат этой длительной операции независмо от того что в этот момент
// будет делать функция main
func somework(ch, out chan int, wait chan bool, i int) chan int {
    start := time.Now().UnixNano()
    go func() {
        rsultOfrollingAction := make(chan int)
        // далее запускаю длительную операцию чтения файла в отдельной горутине
        go func(rsultOfrollingAction chan int) {
            d := make(chan bool) //для сигнализации об окончании длительного действия
            // начало длительной операции в отдельной горутине
            // результат данной работы, при имеющемся коде, не выводится.
            // ДАННЫЙ ВОПРОС ОТНОСТИИТСЯ КАК РАЗ К ЭТОЙ ПРОБЛЕМЕ
            go func(d chan bool) {
                time.Sleep(time.Second * 3)
                text := readf("text.txt")
                rsultOfrollingAction <- len(text)
                // сигнализируем горутине которая должна вывести количество
                // прочитанных строк, что длительная операция завершене
                d <- true
            }(d)
            // когда d получает сигнал об окончании длительной операции
            <-d
            // выводим результат длительной операции.
            fmt.Printf("вложенная в somework горутина завершила выполенение прочитав из файла %v символов\n", <-rsultOfrollingAction)
        }(rsultOfrollingAction)
        // читаем канал из входных аргументов somework
        a := <-ch
        // записываем в вызодной кнал
        out <- a
        // сигнализируем об окончании somesomework()
        wait <- true
    }()
    fmt.Printf("прошло >>%v<< наносекунд с момента начала функции work и функция somework завершилась передав управление main\n", time.Now().UnixNano()-start)
    // возвращаем канал с результататом работы somework
    return out
}

// поростейшее чтение файла.
func readf(name string) []byte {
    f, err := ioutil.ReadFile(name)
    if err != nil {
        fmt.Println(err)
        os.Exit(123)
    }
    return f
}
golang
  • 1 1 个回答
  • 23 Views

1 个回答

  • Voted
  1. Best Answer
    Pak Uula
    2023-10-28T20:39:49Z2023-10-28T20:39:49Z

    第一个问题

    wait您正在错误的函数中写入通道。看看发生了什么:

    main生成somework并继续工作。然后它停下来,等待频道out。

    somework产生一个嵌套的 goroutine 并终止。

    第一个匿名 goroutine 启动,Go 运行时将其表示为main.somework.func1。她

    1. 生成一个匿名 goroutinemain.somework.func1.1
    2. 读取通道in、写入通道out以及
    3. TADAM在频道中记录wait

    2 和 3 之间的某处通过从通道读取来解锁mainout。然后main它再次阻塞,从通道等待wait。与此同时,main它再次醒来,因为 goroutinemain.somework.func1写入了wait.

    main打印一条消息并结束。main.somework.func1.1第二个匿名 Goroutine甚至没有时间启动的概率非零。

    您需要转移wait <- true到等待文件读取完成的 goroutine:

        go func() {
            rsultOfrollingAction := make(chan int)
            // далее запускаю длительную операцию чтения файла в отдельной горутине
            go func(rsultOfrollingAction chan int) {
                d := make(chan bool) //для сигнализации об окончании длительного действия
                // начало длительной операции в отдельной горутине
                // результат данной работы, при имеющемся коде, не выводится.
                // ДАННЫЙ ВОПРОС ОТНОСТИИТСЯ КАК РАЗ К ЭТОЙ ПРОБЛЕМЕ
                go func(d chan bool) {
                    time.Sleep(time.Second * 3)
                    text := readf("sample.bin")
                    rsultOfrollingAction <- len(text)
                    // сигнализируем горутине которая должна вывести количество
                    // прочитанных строк, что длительная операция завершене
                    d <- true
                }(d)
                // когда d получает сигнал об окончании длительной операции
                <-d
                // выводим результат длительной операции.
                fmt.Printf("вложенная в somework горутина завершила выполенение прочитав из файла %v символов\n", <-rsultOfrollingAction)
                wait <- true
            }(rsultOfrollingAction)
            // читаем канал из входных аргументов somework
            a := <-in
            // записываем в вызодной кнал
            out <- a
            // сигнализируем об окончании somesomework()
            // wait <- true
        }()
    

    你有看到?我将条目wait从第一个嵌套 goroutine 移至第二个,因为由于通过d和进行同步,第二个 goroutine 肯定会等到它从文件中读取数据rsultOfrollingAction。应该是她发出漫长的手术结束的信号。

    第二个问题

    这样的同步就形成了死锁。原因是该指令make(chan T)创建了一个零容量的通道。即没有writer则reader被阻塞,没有reader则writer被阻塞。这就是你身上发生的事情:

    • 第二个匿名函数等待通道d( <-d)。由于等待时d第三个 Goroutine 尚未启动,因此第二个 Goroutine 被阻塞。
    • 第三个匿名 goroutine 等待,然后读取文件,然后将结果写入rsultOfrollingAction( rsultOfrollingAction <- len(text))。而且……还被封锁了!由于该频道rsultOfrollingAction目前还没有读者。读取器(第二个 goroutine)在等待时被阻塞d。

    你能为这个做什么?

    计划A。

    把它扔掉d。数据的出现rsultOfrollingAction本身就表明读取操作完成。

    证明: https: //go.dev/play/p/mXxkYgXkyxw

    main等待嵌套 goroutine 完成。

    计划b

    如果通道d本身对你来说很重要,那么你需要使通道rsultOfrollingAction异步:rsultOfrollingAction := make(chan int, 1)

    如果通道是空的,写入者将不会等待读取者出现。写入的值将被写入通道缓冲区,第三个goroutine将安全地继续进行下一个操作d <- true

    证明: https: //go.dev/play/p/4BeRqa2ilJT

    • 2

相关问题

  • windows上的protoc编译错误

  • 递归打印包依赖

  • Golang 算法 XTEA ECB 库“golang.org/x/crypto/xtea”

  • 如何将 IMEI 转换为字节并返回 golang

  • 如何创建文件并将其移动到新目录?

  • go中的函数参数中是否有cv-qualifier的类似物?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5