我在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}
}
}
答案 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