通道关闭后如何关闭 goroutines

How to close goroutines after channel has been shut

我正在尝试编写一个同时挖掘比特币区块的程序。我已经设置好它,以便每个 goroutine 都有一个初始的起始随机数,每个随机数都是 4 的一小部分,即。 2**64 - 1(uint64 类型的最大数量)/1 或 2 或 3 或 4。

这些矿工中只有一个会遇到正确的随机数,当这种情况发生时,我希望它通过一个渠道将其传递给矿工经理,当这种情况发生时,我希望其他 3 名矿工停止他们的工作。正在做。

唯一的问题是我不知道如何销毁 运行 goroutine,或者是否有办法完成我所要求的。

func miner(blockNumber int, transactions string, previousHash string, zeroPrefix string, startNonce uint64, nonceChan chan uint64, hashChan chan string) {
    var text string
    var newHash string

    for {
        text = strconv.Itoa(blockNumber) + transactions + previousHash + strconv.FormatUint(startNonce, 10)
        newHash = encrypt(text)

        if startswith(newHash, zeroPrefix) {
            nonceChan <- startNonce
            hashChan  <- newHash

            close(nonceChan)
            close(hashChan)
            break
        } else {
            startNonce++
        }
    }
}

func mine(blockNumber int, transactions string, previousHash string, zeroPrefix int) Block {
    var prefixString string
    var newHash string
    var nonce uint64
    var startNonce uint64

    nonceChan := make(chan uint64)
    hashChan := make(chan string)

    for i := 0; i < zeroPrefix; i++ {
        prefixString += "0"
    }

    start := time.Now()

    for i := 0; i < 4; i++{
        // This line is for deciding at what nonce value a miner should start at.
        startNonce = uint64((float64(i) / 4) * math.Pow(2, 64))

        go func() {
            fmt.Println("Started miner with start nonce of", startNonce)
            miner(blockNumber, transactions, previousHash, prefixString, startNonce, nonceChan, hashChan)
        }()
    }

    nonce = <- nonceChan
    newHash = <- hashChan

    // Here is where I would like to destroy the other three miners

    block := Block{
        blockNumber,
        transactions,
        previousHash,
        newHash,
        nonce,
        zeroPrefix,
        time.Since(start),
    }

    return block
}

在启动所有goroutines的函数中创建一个ctx, cancel := context.WithCancel(context.Background())并将其传递给所有goroutines(作为函数中的第一个参数)。

当需要取消工作时,调用cancel函数。你可以这样做,例如在收到结果后的 main 函数中。

在每个 goroutine 中使用 select(在你的 for 循环中)检查 ctx.Done:

select {
case <-ctx.Done():
    return
default:
}

// continue mining

示例:

func miner(ctx context.Context, ...) {
    defer func() {
        // any necessary cleanup
    }

    for {
        select {
        case <-ctx.Done():
            // abort was called for: exit
            return
        default:
        }

        // continue mining
    }
}
func mine() {
    // use a single channel to get the result. You could 
    // block yourself if you use multiple channels
    chResult := make(chan result)

    // create context
    ctx, cancel := context.WithCancel(context.Background())

    for i := 0; i < 4; i++{
        // ...

        // pass the context into the miner
        go miner(ctx, chResult, ...)
    }

    // block for first miner to be successful
    res := <-chResult
   
    // cancel the other routines
    cancel()
    
    // ...
}

result 可以是:

struct result {
    hash  string
    nonce uint64
}

你可以使用 context 这是处理 go 例程终止的典型对象之一。

context : 包上下文定义了上下文类型,它携带截止日期、取消信号和跨 API 边界和进程之间的其他请求范围的值。

ctx, cancel := context.WithCancel(context.Background()) 用这个你可以创建一个 contextcancel 函数。

只需在您的 go 例程中传递 ctx and cancel,当您在任何 go 例程中完成后,只需进行 cancel() func() 调用。然后 ctx.done() 将为真,然后 switch 的第一个 case 将为真,它将 return 来自你所有的 go 例程。


func miner( ctx context.Context,  blockNumber int, transactions string, previousHash string, zeroPrefix string, startNonce uint64, nonceChan chan uint64, hashChan chan string) {
    var text string
    var newHash string

    for {
        select {
        case <-ctx.Done():  // if cancel() execute
           return
        default:
            text = strconv.Itoa(blockNumber) + transactions + previousHash + strconv.FormatUint(startNonce, 10)
            newHash = encrypt(text)

            if startswith(newHash, zeroPrefix) {
                nonceChan <- startNonce
                hashChan  <- newHash

                close(nonceChan)
                close(hashChan)
                break
            } else {
                startNonce++
            }

        }

    }
}

func mine(blockNumber int, transactions string, previousHash string, zeroPrefix int) Block {
    var prefixString string
    var newHash string
    var nonce uint64
    var startNonce uint64

    nonceChan := make(chan uint64)
    hashChan := make(chan string)

    for i := 0; i < zeroPrefix; i++ {
        prefixString += "0"
    }

    start := time.Now()
    ctx, cancel := context.WithCancel(context.Background())

    for i := 0; i < 4; i++{
        // This line is for deciding at what nonce value a miner should start at.
        startNonce = uint64((float64(i) / 4) * math.Pow(2, 64))

        go func(ctx context.Context) {
            fmt.Println("Started miner with start nonce of", startNonce)
            miner(ctx, blockNumber, transactions, previousHash, prefixString, startNonce, nonceChan, hashChan)
        }(ctx)
    }

    nonce = <- nonceChan
    newHash = <- hashChan
    cancel()

    // Here is where I would like to destroy the other three miners

    block := Block{
        blockNumber,
        transactions,
        previousHash,
        newHash,
        nonce,
        zeroPrefix,
        time.Since(start),
    }

    return block
}