英文:
How to print numbers in desired order
问题
我在互联网上找到了很多关于互斥锁(mutex)的教程,然后我尝试构建自己的示例。请看下面的代码片段:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
thread := new(sync.Mutex)
y := 0
for i := 0; i < 10; i++ {
go func() {
y = i
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
time.Sleep(100000)
}
作为输出,我只得到了10,但我的目标是得到1、2、3、4...10。我该如何做到这一点?或者说我的示例没有任何意义吗?
英文:
I found many tutorials in internet about mutex, then after i try to build my own sample. Look at my following code snippet.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
thread := new(sync.Mutex)
y := 0
for i := 0; i < 10; i++ {
go func() {
y = i
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
time.Sleep(100000)
}
As output I've got only 10 but my target is to get 1, 2, 3, 4...10. How can I do that? Or my sample does not make any sense?
答案1
得分: 2
问题在于函数闭包绑定到外部作用域变量i
上,而i
在循环过程中会发生变化。当程序执行闭包时(很可能是在循环结束后),i
的值已经变成了10。
有两种解决方法,第一种是在每次循环中创建一个新的变量:
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
或者直接将值传递给函数闭包:
for i := 0; i < 10; i++ {
go func(value int) {
thread.Lock()
fmt.Println(value)
thread.Unlock()
}(i)
}
英文:
the problem is that the function closure is bound to the external scope variable i
And i
changes during the loop. When the routine executes (most likely after the loop finishes) the value of i
is 10.
There are two way of solving this, the first one is to create a new variable on each loop:
http://play.golang.org/p/ylTENWeuEl
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Println(y)
thread.Unlock()
}()
}
or by passing the value directly to the function closure:
http://play.golang.org/p/DKd12-VNSk
for i := 0; i < 10; i++ {
go func(value int) {
thread.Lock()
fmt.Println(value)
thread.Unlock()
}(i)
}
答案2
得分: 2
你的示例没有意义。只需简单地编写:
package main
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
fmt.Println()
}
输出:
0 1 2 3 4 5 6 7 8 9
在修复了你的程序中的错误之后,很容易看出使用 goroutine 不会提供有序的结果。
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() + 2)
thread := new(sync.Mutex)
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Print(y, " ")
thread.Unlock()
}()
}
time.Sleep(time.Second)
fmt.Println()
}
输出:
1 0 5 6 2 7 8 9 3 4
Playground:
http://play.golang.org/p/FQdrL3z-hR
英文:
Your example doesn't make sense. Simply write:
package main
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
fmt.Println()
}
Output:
0 1 2 3 4 5 6 7 8 9
After fixing the bugs in your program, it's easy to see that using goroutines won't provide an ordered result.
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() + 2)
thread := new(sync.Mutex)
for i := 0; i < 10; i++ {
y := i
go func() {
thread.Lock()
fmt.Print(y, " ")
thread.Unlock()
}()
}
time.Sleep(time.Second)
fmt.Println()
}
Output:
1 0 5 6 2 7 8 9 3 4
Playground:
答案3
得分: 1
尝试:
for i := 0; i < 10; i++ {
go func(j int) {
thread.Lock()
fmt.Println(j)
thread.Unlock()
}(i)
}
这里是原因。
英文:
Try :
for i := 0; i < 10; i++ {
go func(j int) {
thread.Lock()
fmt.Println(j)
thread.Unlock()
}(i)
}
Here is why.
答案4
得分: 1
如peterSO已经指出,如果你只是想按顺序打印,不需要并发(Go协程)和互斥锁:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
但是,如果你想并发地进行一些计算,然后按照所需的顺序打印结果,你可以通过使用通道来解决,让主例程负责按顺序打印,或者通过使用sync.Cond
来解决,如下面的示例所示:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
c := sync.NewCond(new(sync.Mutex))
var printed int
for i := 0; i < 10; i++ {
wg.Add(1)
go func(y int) {
// 执行一些耗时的计算,需要 y*y%10 毫秒
time.Sleep(time.Duration(y*y%10) * time.Millisecond)
c.L.Lock()
// 在循环中等待,直到轮到该 goroutine
for printed != y {
c.Wait()
}
fmt.Printf("%d ", y)
printed++
c.L.Unlock()
// 通知等待的例程检查是否轮到它们
c.Broadcast()
wg.Done()
}(i)
}
wg.Wait()
}
输出:
0 1 2 3 4 5 6 7 8 9
Playground: http://play.golang.org/p/k7tUfKPRxW
我使用了sync.WaitGroup
而不是time.Sleep(time.Second)
来防止主例程过早退出。尽管对于sync.Cond
解决方案来说不是必需的。
英文:
As peterSO already pointed out, if you simply want to print in order, concurrency (Go routines) and mutexes aren't needed:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
But, if you want to do some calculation concurrently, and then print the results in the desired order, you can solve it by using a channel and have the main routine take care of the printing in order, or by using a sync.Cond
as in the example below:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
c := sync.NewCond(new(sync.Mutex))
var printed int
for i := 0; i < 10; i++ {
wg.Add(1)
go func(y int) {
// Do some costly calculation that cakes y*y%10 milliseconds
time.Sleep(time.Duration(y*y%10) * time.Millisecond)
c.L.Lock()
// Wait in a loop til it is this goroutine's turn
for printed != y {
c.Wait()
}
fmt.Printf("%d ", y)
printed++
c.L.Unlock()
// Tell the waiting routines to check if it is their turn
c.Broadcast()
wg.Done()
}(i)
}
wg.Wait()
}
Output:
0 1 2 3 4 5 6 7 8 9
Playground: http://play.golang.org/p/k7tUfKPRxW
I used sync.WaitGroup
instead of time.Sleep(time.Second)
to prevent main from exiting prematurely. It is not required for the sync.Cond
solution though.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论