我目前正试图通过range
map
来执行并发数据库请求,而不是同步,显然是因为速度提升。
我的问题是我有这样的事情:
var mainthreads = make(chan *mainthread)
var mainthreadsFetched = make(chan struct{})
for containerid := range containers {
go func() {
rows, err := db.Query("SELECT thread_id, belongs_to, thread_name, access_level FROM forum_mainthread WHERE belongs_to = ?", containerid)
defer rows.Close()
if err != nil {
log.Println(err)
}
for rows.Next() {
mainthread := &MainThread{}
err := rows.Scan(&mainthread.MainThreadID, &mainthread.BelongsTo, &mainthread.ThreadName, &mainthread.AccessLevel)
if err != nil {
log.Println(err)
}
mainthreads <- mainthread
}
}()
mainthreadsFetched <- struct{}{}
}
// Get all mainthreads
<-mainthreadsFetched
// Do other stuff after complete
显然mainthreadsFetched <- struct{}{}
几乎立即被调用,因为循环完成的速度比你能闪烁的速度快,我怎样才能为每个循环创建一个新的通道,不会阻止每个新的goroutine
启动,而是让循环开始全部goroutines
,然后在每个goroutine
完成后发送到频道。
答案 0 :(得分:3)
使用sync.WaitGroup
是一个很好的解决方案,也是通常使用的解决方案。
或者,你可以在mainthreadsFetched
len(containers)
次收到,而不是只收到1.这将保证所有的例程都已完成。您需要将发送行移动到go例程的末尾(或者,更好的是,延迟)。
此外,由于containerid
位于for循环中,因此其值会发生变化。您需要将它作为参数传递给go例程闭包。
答案 1 :(得分:0)
因此,我能做到这一点的最佳方法是使用sync.WaitGroup
并执行以下操作:
var wg sync.WaitGroup
var mainThreadFetched = make(chan MainThread)
for containerid := range containers {
wg.Add(1)
go func(containerid int64) {
rows, err := db.Query("SELECT thread_id, belongs_to, thread_name, access_level FROM forum_mainthread WHERE belongs_to = ?", containerid)
defer rows.Close()
if err != nil {
log.Println(err)
}
for rows.Next() {
mainthread := MainThread{}
err := rows.Scan(&mainthread.MainThreadID, &mainthread.BelongsTo, &mainthread.ThreadName, &mainthread.AccessLevel)
if err != nil {
log.Println(err)
}
mainThreadFetched <- mainthread
}
wg.Done()
}(containerid)
}
go func() {
wg.Wait()
close(mainThreadFetched)
}()
for mainthread := range mainThreadFetched {
containers[mainthread.BelongsTo].MainThreads = append(containers[mainthread.BelongsTo].MainThreads, mainthread)
}
// Do other stuff
现在我可以从mainThreadFetched
频道阅读,然后当WaitGroup
满意时它将关闭频道,允许循环结束并继续
答案 2 :(得分:0)
我不知道你在哪里阅读主线程。如果它不是缓冲通道,您需要以某种方式解决。我将提供一些解决方案 - 没有更多&#34;正确&#34;比另一个 - 它只取决于你的需求。
变体A 这是最简单的解决方案,但它假设你有一些其他的goroutine阅读主线程(可能已经是这种情况)
var mainthreads = make(chan *mainthread)
var mainthreadsFetched = make(chan struct{})
go somethingWhichReadsMainThreads()
for containerid := range containers {
go func(containerid int) {
// build query omitted for brevity
for rows.Next() {
// omitted for brevity
mainthreads <- mainthread
}
mainthreadsFetched <- struct{}{}
}(containerid)
}
for i := 0; i < len(containers); i++ {
<-mainThreadsFetched
}
close(mainthreads)
// Do other stuff after complete
变体B 这个使用select语句来处理与完成通知分开读取线程而不需要另外的goroutine。
var mainthreads = make(chan *mainthread)
var mainthreadsFetched = make(chan struct{})
for containerid := range containers {
go func(containerid int) {
// build query omitted for brevity
for rows.Next() {
// omitted for brevity
mainthreads <- mainthread
}
mainthreadsFetched <- struct{}{}
}(containerid)
}
numComplete := 0
readRunning := true
for readRunning {
select {
case thread := <-mainthreads:
// do something with thread, like threads = append(threads, thread)
case <-mainthreadsFetched:
numFetched++
if numFetched == len(containers) {
readRunning = False
}
}
}
// Do other stuff after complete
变体C 这个使用的事实是你没有使用零值&#39; (nil)用于传递实际数据,因此您可以将其用作信号值而不是单独的结构通道。它的优点是代码少得多,但它确实感觉像是远处的怪异动作。
var mainthreads = make(chan *mainthread)
for containerid := range containers {
go func(containerid int) {
// build query omitted for brevity
for rows.Next() {
// omitted Scan for brevity
mainthreads <- mainthread
}
mainthreads <- nil // nil signals to us we are done
}(containerid)
}
numComplete := 0
for thread := range mainthreads {
if thread != nil {
// do something with thread, like threads = append(threads, thread)
} else {
numFetched++
if numFetched == len(containers) {
break
}
}
}
// Do other stuff after complete