>  기사  >  백엔드 개발  >  세 번째 고루틴 내에서 두 고루틴의 완료 상태를 추적하기 위한 Golang의 모범 사례는 무엇입니까?

세 번째 고루틴 내에서 두 고루틴의 완료 상태를 추적하기 위한 Golang의 모범 사례는 무엇입니까?

WBOY
WBOY앞으로
2024-02-11 14:54:09780검색

Golang 中跟踪第三个 Goroutine 中两个 Goroutine 的完成状态的最佳实践是什么?

세 번째 고루틴에서 두 고루틴의 완료 상태를 추적하기 위한 Golang의 모범 사례는 무엇입니까? Golang에서 두 고루틴의 완료 상태를 추적하고 그 결과를 세 번째 고루틴에서 처리하려면 동기화 패키지에서 WaitGroup을 사용하는 것이 가장 좋습니다. WaitGroup을 사용하면 다른 고루틴이 완료될 때까지 기본 고루틴에서 기다릴 수 있습니다. 먼저, WaitGroup 개체를 생성하고 기본 고루틴에서 Add 메서드를 호출하여 대기 중인 고루틴 수를 설정해야 합니다. 그런 다음 각 고루틴이 끝날 때 Done 메서드가 호출되어 해당 고루틴의 완료를 알립니다. 마지막으로 세 번째 고루틴에서는 Wait 메소드가 호출되어 모든 고루틴이 완료될 때까지 기다립니다. 이런 방식으로 우리는 두 고루틴의 결과를 안전하게 추적하고 처리할 수 있습니다. 이는 여러 고루틴의 완료 상태를 추적하기 위한 Golang의 모범 사례입니다.

질문 내용

3개의 고루틴이 동시에 실행되고 있습니다. 그 중 두 개는 처리를 수행하고 결과를 결과 채널로 보냅니다. 세 번째 고루틴은 결과 채널을 읽어 결과를 "계산"합니다. 대기 그룹을 사용하여 두 계산 고루틴이 완료될 때까지 기다린 다음 결과 채널을 반복하여 결과를 계산할 수 있지만 이는 확장되지 않으며 엄청난 버퍼 크기로 버퍼링된 결과 채널을 만들어야 하는데 이는 허용되지 않습니다. 프로덕션 코드에서.

처리가 진행되는 동안 결과를 계산하고 싶지만 모든 계산이 완료되기 전에 프로그램을 종료하고 싶지 않습니다. Go에서 이를 달성하기 위한 모범 사례는 무엇입니까?

이것이 현재 제가 사용하는 방법이며 훌륭하게 작동합니다. 조금 투박해 보이는데 더 좋은 방법이 있는지 궁금합니다.

package main

import (
    "fmt"
    "sync"
)

type T struct{}

func main() {
    var widgetInventory int = 1000
    transactions := make(chan int, 100)
    salesDone := make(chan T)
    purchasesDone := make(chan T)
    var wg sync.WaitGroup
    fmt.Println("Starting inventory count = ", widgetInventory)

    go makeSales(transactions, salesDone)
    go newPurchases(transactions, purchasesDone)

    wg.Add(1)

    go func() {
        salesAreDone := false
        purchasesAreDone := false

        for {
            select {
            case transaction := <-transactions:
                widgetInventory += transaction
            case <-salesDone:
                salesAreDone = true
            case <-purchasesDone:
                purchasesAreDone = true
            default:
                if salesAreDone && purchasesAreDone {
                    wg.Done()
                    return
                }
            }
        }
    }()

    wg.Wait()
    fmt.Println("Ending inventory count = ", widgetInventory)
}

func makeSales(transactions chan int, salesDone chan T) {
    for i := 0; i < 3000; i++ {
        transactions <- -100
    }

    salesDone <- struct{}{}
}

func newPurchases(transactions chan int, purchasesDone chan T) {
    for i := 0; i < 3000; i++ {
        transactions <- 100
    }

    purchasesDone <- struct{}{}
}

해결책

은 어떤 합리적인 정의괜찮아에 맞지 않습니다. 여기에 인기 있는 for 루프가 있습니다:

으아악

읽을 채널이 없으면 default사건이 실행됩니다. 채널이 작동하는 방식 때문에 이런 일이 많이 발생합니다.

약간 수정된 코드 버전은 이 루프의 "열"을 보여줍니다. 정확한 결과는 다양하며 상당히 높을 수 있습니다.

으아악

기본값이 채널의 항목을 차단하지 않는 한 채널에서 선택할 때 selecting 来自通道时,您不希望出现 default 사례를 원하지 않습니다. 그렇지 않으면 이와 같은 열 순환이 발생합니다.

더 나은 방법: 선택을 위해 nil가능한 채널을 사용

일반적으로 선택 항목에서 닫힌 채널을 식별하고 채널 변수를 nilselect 永远不会成功地从 nil

로 설정하려는 경우 select

채널에서 성공적으로 읽히지 않으므로 선택 항목이 효과적으로 "비활성화"됩니다.

코드의 수정된 버전

을 고려해보세요: salesDonepurchasesDone 都被“发出信号”,我们 close(transactions)。一旦我们耗尽 transactions 并且它被关闭,我们将 transactions 设置为 nil。我们在 transactions 不为 nil 时循环,在这段代码中,意味着所有通道都是 nil 으아악

소비자에 대한 이러한 조정으로 더 이상 핫 루프가 없으며 채널에서 데이터를 읽을 때까지 항상 차단됩니다. salesDonepurchasesDone이 모두 "신호"되면 거래를 종료합니다. 트랜잭션을 소진하고

main 共享范围。否则,将 transactions 设置为 nil 将写入一个在 goroutine 之间共享的变量。然而在这种情况下,无论如何,这并不重要,因为我们“知道”我们是最后一个从 transactions 종료되면 트랜잭션을 nil로 설정합니다. transactions가 nil이 아닐 때 반복합니다. 이는 이 코드에서 모든 채널이

임을 의미합니다.

미묘하지만 중요한 점: 이 함수에 채널을 전달하므로 해당 참조가 main과 범위를 공유하지 않습니다. 그렇지 않은 경우 transactions

로 설정하면 고루틴 간에 공유되는 변수에 기록됩니다. 그러나 이 경우에는 우리가 트랜잭션에서 마지막으로 읽은 것임을 "알기" 때문에 어쨌든 문제가 되지 않습니다. transactions 的生产。然后你想排空 transactions。一旦通道关闭并排空,main

더 쉬운 옵션: 여러 대기 그룹

select 来执行此操作。而 select여기서 무엇을 하고 있는지 생각해보면 두 프로듀서가 페어링을 완료할 때까지 기다려야

합계가 완료되었다는 사실을 알기 전에 기다려야 합니다.

각 "작업자"에 대한 케이스를 🎜 가질 필요는 없습니다. 이는 꽤 우아하지 않은 일이지만, 여러 작업자를 하드코딩하고 "완료" 채널을 개별적으로 처리해야 합니다. 🎜 🎜당신이 해야 할 일은:🎜
  • 除了为生产者使用一个 var resultswgsync.WaitGroup 之外,还为消费者添加一个。
  • 生产者 defer wg.Done()
  • 消费者 defer resultswg.Done() 在遍历 transactions 之前:
    go func() {
        defer resultswg.Done()
        for transaction := range transactions {
            widgetInventory += transaction
        }
    }()
  • main 处理等待生产者、关闭事务以结束范围,然后等待消费者:
    wg.Wait()
    close(transactions)
    resultswg.Wait()

以这种方式编码,最终会变得简短而甜蜜

package main

import (
    "fmt"
    "sync"
)

func main() {
    var widgetInventory int = 1000
    transactions := make(chan int, 100)

    var wg, resultswg sync.WaitGroup
    fmt.Println("Starting inventory count = ", widgetInventory)
    wg.Add(2)

    go makeSales(transactions, &wg)
    go newPurchases(transactions, &wg)
    resultswg.Add(1)
    go func() {
        defer resultswg.Done()
        for transaction := range transactions {
            widgetInventory += transaction
        }
    }()

    wg.Wait()
    close(transactions)
    resultswg.Wait()
    fmt.Println("Ending inventory count = ", widgetInventory)
}

func makeSales(transactions chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 3000; i++ {
        transactions <- -100
    }

}

func newPurchases(transactions chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 3000; i++ {
        transactions <- 100
    }

}

您可以在这里看到,在此模式中可以有任意数量的生产者;您只需为每个生产者添加 wg.Add(1) 即可。

当我不知道每个工作人员会返回多少结果时,我一直使用这种模式来并行化工作。我发现它很容易理解,并且比尝试 select 多个通道简单得多。事实上,我什至想说,如果您发现自己从多个渠道进行 selecting,您应该退后一步,确保它对您来说确实有意义。我使用 select 的频率远远低于使用等待组的频率。

위 내용은 세 번째 고루틴 내에서 두 고루틴의 완료 상태를 추적하기 위한 Golang의 모범 사례는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 stackoverflow.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제