【问题标题】:How to implement a counter when using golang's goroutine?使用golang的goroutine时如何实现计数器?
【发布时间】:2012-09-04 14:37:20
【问题描述】:

我正在尝试创建一个具有推送和弹出功能的队列结构。

我需要使用 10 个线程推送和另外 10 个线程弹出数据,就像我在下面的代码中所做的那样。

问题:

  1. 我需要打印出我推送/弹出的数量,但我不知道该怎么做。
  2. 有什么方法可以加快我的代码速度吗?代码对我来说太慢了。
package main

import (
    "runtime"
    "time"
)

const (
    DATA_SIZE_PER_THREAD = 10000000
)

type Queue struct {
    records string
}


func (self Queue) push(record chan interface{}) {
    // need push counter
    record <- time.Now()
}

func (self Queue) pop(record chan interface{}) {
    // need pop counter
    <- record
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    //record chan
    record := make(chan interface{},1000000)
    //finish flag chan
    finish := make(chan bool)
    queue := new(Queue)
    for i:=0; i<10; i++ {
        go func() {
            for j:=0; j<DATA_SIZE_PER_THREAD; j++ {
                queue.push(record)
            }
            finish<-true
        }()
    }
    for i:=0; i<10; i++ {
        go func() {
            for j:=0; j<DATA_SIZE_PER_THREAD; j++ {
                queue.pop(record)
            }
            finish<-true
        }()
    }
    for i:=0; i<20; i++ {
        <-finish
    }
}

【问题讨论】:

    标签: go


    【解决方案1】:

    有一些事情你应该解决。

    • Queue 类型的方法应该有指针接收器。否则,每个方法 call 将创建当前队列类型的副本,并且对队列字段的任何更改都将 不会在方法调用本身之外持续存在。

    • 等待所有例程完成,可以使用sync.WaitGroup 完成。这 正是它的设计目的。

    • 在队列类型中维护线程安全的推送/弹出计数器可以通过以下方式完成 使用sync/atomic 包。

    就速度而言,从您的示例来看,我不太确定您要达到的目标。如果您稍微详细说明一下,可能会出现任何优化。

    这是我根据您的代码修改的示例:

    package main
    
    import (
        "log"
        "runtime"
        "sync"
        "sync/atomic"
        "time"
    )
    
    const SizePerThread = 10000000
    
    type Queue struct {
        records string
        count   int64
    }
    
    func (q *Queue) push(record chan interface{}) {
        record <- time.Now()
    
        newcount := atomic.AddInt64(&q.count, 1)
        log.Printf("Push: %d", newcount)
    }
    
    func (q *Queue) pop(record chan interface{}) {
        <-record
    
        newcount := atomic.AddInt64(&q.count, -1)
        log.Printf("Pop: %d", newcount)
    }
    
    func main() {
        var wg sync.WaitGroup
    
        runtime.GOMAXPROCS(runtime.NumCPU())
    
        record := make(chan interface{}, 1000000)
        queue := new(Queue)
    
        // We are launching 20 goroutines.
        // Let the waitgroup know it should wait for as many
        // of them to finish.
        wg.Add(20)
    
        for i := 0; i < 10; i++ {
            go func() {
                defer wg.Done()
    
                for j := 0; j < SizePerThread; j++ {
                    queue.push(record)
                }
            }()
    
            go func() {
                defer wg.Done()
    
                for j := 0; j < SizePerThread; j++ {
                    queue.pop(record)
                }
            }()
        }
    
        // Wait for all goroutines to finish.
        wg.Wait()
    }
    

    【讨论】:

    • 谢谢@jimt,但我不太明白你所说的队列类型应该有指针接收器是什么意思,你能给我一个关于golang's doc的链接吗?我找不到,再次感谢。
    • @MrROY:Effective Go 的这一部分讨论了不同之处:golang.org/doc/effective_go.html#pointers_vs_values。本质上,如果接收者是 (T),您的方法将获得调用它的对象的 副本,因此无法更改它。如果接收者是 (*T),它会得到一个指向它的指针。
    • 如果在编译时并不严格知道 go-routines 的数量,是否有任何理由不在每个创建的 goroutine 中调用 wg.Add(1) defer.wg.Done()
    • @matthias:完全没有理由。我使用了一次 Add() 调用,因为事先知道例程的数量,因此能够减少必要的函数调用量。
    • @matthias:我可能应该修改我的评论。在每个 goroutine 中调用 wg.Add(1) 原则上是可行的。但需要注意的是,在调用线程到达wg.Wait()部分之前,并不能保证goroutine会被调度执行。在极少数情况下,这可能意味着WaitGroup 计数器尚未增加,对wg.Wait() 的调用提前完成。这构成了竞争条件,应该避免。不过,您可以在启动给定的 goroutine 之前安全地调用 wg.Add(1)
    【解决方案2】:

    问题 1 的答案:正如 jimt 所建议的,sync/atomic 具有自动更新计数器的功能,这可能对您有用。

    问题2的答案:减小DATA_SIZE_PER_THREAD的值,或者更好的是,使用程序

    package main
    func main() {}
    

    它以更有效的方式产生与您的程序相同的输出。

    说真的,我知道您编写了一个小程序来探索一些概念。但是,您的程序包含许多问题。现在不是担心速度的时候,而是学习一些基本概念的时候。

    【讨论】:

    • 根本不是一个有用的答案。
    猜你喜欢
    • 2014-07-19
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 2013-09-09
    • 1970-01-01
    • 2022-06-10
    • 2019-07-16
    相关资源
    最近更新 更多