jjzjj

去死锁所有 goroutines 睡着了

coder 2024-07-13 原文

这是我之前帖子的后续:

    http://stackoverflow.com/questions/34736825/goroutine-exit-status-2-what-does-it-mean-why-is-it-happening?noredirect=1#comment57238789_34736825

在阅读了 SO 内外的多个主题和文章之后,我仍然无法弄清楚应该在哪里关闭 channel 。

该程序将打开一个文件列表,为每个输入文件创建一个输出文件(具有相同的名称),访问每个输入文件中的所有 url 并从中获取所有 href 链接 - 这些链接将保存到相应的输出文件。 但是,我收到以下错误:

    http://play.golang.org/p/8X-1rM3aXC

linkgetter、getHref函数主要是处理。 Head 和 tail 作为单独的 goroutines 运行,由 worker 进行处理。

    package main

    import (
    "bufio"
    "bytes"
    "fmt"
    "golang.org/x/net/html"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "path/filepath"
    "regexp"
    "sync"
    )

    type Work struct {
    Link     string
    Filename string
    }

    type Output struct {
    Href     string
    Filename string
    }

    func getHref(t html.Token) (href string, ok bool) {
    // Iterate over all of the Token's attributes until we find an    "href"
    for _, a := range t.Attr {
            if a.Key == "href" {
                    href = a.Val
                    ok = true
            }
    }
    return
    }

    func linkGetter(out chan<- Output, r io.Reader, filename string) {
    z := html.NewTokenizer(r)
    for {
            tt := z.Next()
            switch {
            case tt == html.ErrorToken:
                    return
            case tt == html.StartTagToken:
                    t := z.Token()
                    isAnchor := t.Data == "a"
                    if !isAnchor {
                            continue
                    }

                    // Extract the href value, if there is one
                    url, ok := getHref(t)
                    if !ok {
                            continue
                    }

                    out <- Output{url, filename}
            }
    }
    }

    func worker(out chan<- Output, in <-chan Work, wg *sync.WaitGroup)    {
    defer wg.Done()
    for work := range in {
            resp, err := http.Get(work.Link)
            if err != nil {
                    continue
            }
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                    continue
            }
            if err = resp.Body.Close(); err != nil {
                    fmt.Println(err)
            }
            linkGetter(out, bytes.NewReader(body), work.Filename)
    }
    }

    func head(c chan<- Work) {
    r, _ := regexp.Compile("(.*)(?:.json)")
    files, _ := filepath.Glob("*.json")

    for _, elem := range files {
            res := r.FindStringSubmatch(elem)
            for k, v := range res {

                    if k == 0 {
                            outpath, _ :=  filepath.Abs(fmt.Sprintf("go_tester/%s", v))

                            abspath, _ := filepath.Abs(fmt.Sprintf("url_links/%s", v))
                            f, _ := os.Open(abspath)
                            scanner := bufio.NewScanner(f)

                            for scanner.Scan() {
                                    c <- Work{outpath, scanner.Text()}
                            }

                    }
            }

    }


    }

    func tail(c <-chan Output) {
    currentfile := ""
    var f *os.File
    var err error
    for out := range c {
            if out.Filename != currentfile {
                    if err = f.Close(); err != nil { // might cause an error on first run
                            fmt.Println(err)
                    }
                    f, err = os.OpenFile(out.Filename, os.O_APPEND|os.O_WRONLY, 0600)
                    if err != nil {
                            log.Fatal(err)
                    }
                    currentfile = out.Filename
            }
            if _, err = f.WriteString(out.Href + "\n"); err != nil {
                    fmt.Println(err)
            }
    }

    }

    const (
    nworkers = 80
    )

    func main() {
    //fmt.Println("hi")
    in := make(chan Work)
    out := make(chan Output)

    go head(in)
    go tail(out)

    var wg sync.WaitGroup
    for i := 0; i < 85; i++ {
            wg.Add(1)
            go worker(out, in, &wg)
    }
    close(in)   
    close(out)    
    wg.Wait()


    }

channel 关闭的方式有什么问题?

最佳答案

您并没有真正关注此处的流水线设计。你必须问自己“X 部分什么时候完成?完成时应该发生什么?完成后会发生什么?”对于管道的每个部分。

您启动了 headtailworker 来对 channel 进行测距。这些函数成功返回的唯一方法是关闭这些 channel 。

根据需要画出它。

  1. head(in) 馈入 in
  2. worker(out, in, &wg) 遍历 in,输入 out,并告诉您它已通过 完成wg 一旦 in 关闭
  3. tail(out) 范围超过 out

那么你需要做什么来:

  1. 确保所有输入都得到处理?
  2. 确保所有 goroutine 返回?

像这样:

  1. 一旦处理完所有文件,您需要从head 关闭in
  2. 这将导致 worker 在它可以从 in 获得的所有项目都被处理后实际返回,从而导致 wg.Wait() 返回
  3. 现在关闭 out 是安全的,因为没有任何东西进入它,这将导致 tail 最终返回。

但对于这个特定的设计,您可能需要另一个与 tail 关联的 sync.WaitGroup,因为整个程序将在 wg.Wait() 时退出 返回,因此可能没有完成 tail 正在做的所有工作。 See here .具体来说:

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

您可能还想使用缓冲 channel referenced here以帮助避免在 goroutine 之间切换执行太多。使用您当前的设计,您会在上下文切换上浪费大量时间。

关于去死锁所有 goroutines 睡着了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34756537/

有关去死锁所有 goroutines 睡着了的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  3. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  4. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  5. ruby - 如何遍历 Ruby 中所有正则表达式匹配的字符串? - 2

    我们有一个字符串:“”这个正则表达式://i如何从当前字符串中获取所有匹配项? 最佳答案 "".scan(//)参见scan在ruby​​-docs上 关于ruby-如何遍历Ruby中所有正则表达式匹配的字符串?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6857852/

  6. ruby-on-rails - 在所有延迟的作业之前 Hook - 2

    是否可以在所有delayed_job任务之前运行一个方法?基本上,我们试图确保每个运行delayed_job的服务器都有我们代码的最新实例,所以我们想运行一个方法来在每个作业运行之前检查它。(我们已经有了“check”方法并在别处使用它。问题只是关于如何从delayed_job中调用它。) 最佳答案 现在有一种官方方法可以通过插件来做到这一点。这篇博文通过示例清楚地描述了如何执行此操作http://www.salsify.com/blog/delayed-jobs-callbacks-and-hooks-in-rails(本文中描述

  7. ruby - 如何捕获 ruby​​ 中的所有异常? - 2

    我们如何捕获或/和处理ruby​​中所有未处理的异常?例如,这样做的动机可能是将某种异常记录到不同的文件或发送电子邮件给系统管理。在Java中我们会做Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandlerex);在Node.js中process.on('uncaughtException',function(error){/*code*/});在PHP中register_shutdown_function('errorHandler');functionerrorHandler(){$error=error_

  8. ruby - 递归地将所有数字字符串转换为 Ruby 哈希中的整数 - 2

    我有一个随机大小的散列,它可能有类似"100"的值,我想将其转换为整数。我知道我可以使用value.to_iifvalue.to_i.to_s==value来做到这一点,但我不确定我将如何在我的散列中递归地做到这一点,考虑到一个值可以是一个字符串,或一个数组(哈希或字符串),或另一个哈希。 最佳答案 这是一个非常简单的递归实现(尽管必须同时处理数组和散列会增加一些技巧)。deffixnumifyobjifobj.respond_to?:to_i#IfwecancastittoaFixnum,doit.obj.to_ielsifobj

  9. ruby - 如何捕获所有 HTTP 流量(本地代理) - 2

    我希望访问我机器上的所有HTTP流量(我的Windows机器-不是服务器)。据我了解,拥有一个本地代理是所有流量路线的必经之路。我一直在谷歌搜索但未能找到任何资源(关于Ruby)来帮助我。非常感谢任何提示或链接。 最佳答案 WEBrick中有一个HTTP代理(Rubystdlib的一部分)和here's一个实现示例。如果你喜欢生活在边缘,还有em-proxy伊利亚·格里戈里克。这postIlya暗示它似乎确实需要一些调整来解决您的问题。 关于ruby-如何捕获所有HTTP流量(本地代理)

  10. ruby - 我可以使用 RVM 为所有用户维护单一版本的 Ruby 吗? - 2

    我喜欢RVM。我意识到它的主要用例是让不同的用户在不同版本的Ruby之间切换。但是假设我正在将Rails应用程序部署到服务器,并且我只想运行单个版本的Ruby。特别是,我想要1.9.2,用RVM安装它很容易,但没有它就很痛苦。有没有一种方法可以让我说“我希望这是所有用户的规范Ruby安装”(连同它的所有gem),而不必手动创建一堆符号链接(symboliclink)并在每次更新到更新时更改它们Ruby版本? 最佳答案 以root身份安装RVM并执行sudorvmuse1.9.2--default。任何采购/usr/local/rvm

随机推荐