如何在golang中脱颖而出

时间:2014-02-14 15:32:42

标签: go channels

我在golang中有一个程序,它计算SHA1并打印以两个零开头的程序。我想使用goroutines和渠道。我的问题是,如果我不知道它会产生多少结果,我不知道如何优雅地退出select子句。

许多教程提前知道并在反击时退出。其他建议使用WaitGroups,但我不想这样做:我想在主要线程中显示结果,只要它出现在频道中。有人建议在完成goroutine时关闭一个频道,但我想在异步完成后关闭它,所以我不知道如何。

请帮助我达到我的要求:

package main

import (
    "crypto/sha1"
    "fmt"
    "time"
    "runtime"
    "math/rand"
)

type Hash struct {
    message string
    hash [sha1.Size]byte

}

var counter int = 0
var max int = 100000
var channel = make(chan Hash)
var source = rand.NewSource(time.Now().UnixNano())
var generator = rand.New(source)

func main() {
    nCPU := runtime.NumCPU()
    runtime.GOMAXPROCS(nCPU)
    fmt.Println("Number of CPUs: ", nCPU)
    start := time.Now()

    for i := 0 ; i < max ; i++ {
        go func(j int) {
            count(j)
        }(i)
    }
    // close channel here? I can't because asynchronous producers work now

    for {
        select {
                    // how to stop receiving if there are no producers left?
            case hash := <- channel:
                fmt.Printf("Hash is %v\n ", hash)
            }
    }
    fmt.Printf("Count of %v sha1 took %v\n", max, time.Since(start))
}

func count(i int) {
    random := fmt.Sprintf("This is a test %v", generator.Int())
    hash := sha1.Sum([]byte(random))

    if (hash[0] == 0 && hash[1] == 0) {
        channel <- Hash{random, hash}
    }
}

2 个答案:

答案 0 :(得分:2)

应该重新检查程序的结构。 这是我认为你正在寻找的一个工作例子。 它可以在Go playground

上运行
package main

import (
    "crypto/sha1"
    "fmt"
    "math/rand"
    "runtime"
    "time"
)

type Hash struct {
    message string
    hash    [sha1.Size]byte
}

const Max int = 100000

func main() {
    nCPU := runtime.NumCPU()
    runtime.GOMAXPROCS(nCPU)

    fmt.Println("Number of CPUs: ", nCPU)

    hashes := Generate()
    start := time.Now()

    for hash := range hashes {
        fmt.Printf("Hash is %v\n ", hash)
    }

    fmt.Printf("Count of %v sha1 took %v\n", Max, time.Since(start))
}

func Generate() <-chan Hash {
    c := make(chan Hash, 1)

    go func() {
        defer close(c)

        source := rand.NewSource(time.Now().UnixNano())
        generator := rand.New(source)

        for i := 0; i < Max; i++ {
            random := fmt.Sprintf("This is a test %v", generator.Int())
            hash := sha1.Sum([]byte(random))

            if hash[0] == 0 && hash[1] == 0 {
                c <- Hash{random, hash}
            }
        }
    }()

    return c
}

编辑:这不会为每个哈希计算启动一个单独的例程, 但说实话,我没有看到这样做的价值。所有这些例程的调度可能比在单个例程中运行代码花费更多。 如果需要,您可以将其拆分为N个例程的块,但是1:1映射不是这样的。

答案 1 :(得分:2)

首先:如果不知道你的计算何时结束,你怎么能模拟它?确保您确切知道程序何时以及在何种情况下终止。如果你完成了,你就知道如何用代码编写它。

你基本上处理的是生产者 - 消费者问题。标准案例。我会模特儿 这样(on play):

生产者

func producer(max int, out chan<- Hash, wg *sync.WaitGroup) {
    defer wg.Done()

    for i := 0; i < max; i++ {
        random := fmt.Sprintf("This is a test %v", rand.Int())
        hash := sha1.Sum([]byte(random))

        if hash[0] == 0 && hash[1] == 0 {
            out <- Hash{random, hash}
        }
    }

    close(out)
}

显然你是强制性的哈希,所以当循环结束时就会达到目的。 我们可以关闭这里的频道,并向其他goroutines发出信号,表示没有什么可以听的了。

消费

func consumer(max int, in <-chan Hash, wg *sync.WaitGroup) {
    defer wg.Done()

    for {
        hash, ok := <-in

        if !ok {
            break
        }

        fmt.Printf("Hash is %v\n ", hash)
    }
}

消费者从in频道获取所有传入消息并检查它是否已关闭(ok)。 如果关闭,我们就完成了。否则打印收到的哈希值。

主要

要开始这一切,我们可以写:

wg := &sync.WaitGroup{}
c := make(chan Hash)

wg.Add(1)
go producer(max, c, wg)

wg.Add(1)
go consumer(max, c, wg)

wg.Wait()

WaitGroup的目的是等到产生的goroutine完成,发出信号 {/ 1}}在goroutines中的召唤。

旁注

另请注意,您使用的wg.Done对于并发访问并不安全。使用初始化的 全球Rand。例如:

math/rand