如何按照所需的顺序打印数字

huangapple go评论82阅读模式
英文:

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 (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func main() {

	thread := new(sync.Mutex)
	y := 0

	for i := 0; i &lt; 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 &lt; 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 &lt; 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 (
	&quot;fmt&quot;
)

func main() {
	for i := 0; i &lt; 10; i++ {
		fmt.Print(i, &quot; &quot;)
	}
	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 (
	&quot;fmt&quot;
	&quot;runtime&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU() + 2)
	thread := new(sync.Mutex)
	for i := 0; i &lt; 10; i++ {
		y := i
		go func() {
			thread.Lock()
			fmt.Print(y, &quot; &quot;)
			thread.Unlock()
		}()
	}
	time.Sleep(time.Second)
	fmt.Println()
}

Output:

1 0 5 6 2 7 8 9 3 4 

Playground:

http://play.golang.org/p/FQdrL3z-hR

答案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 &lt; 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 &lt; 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 (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)
func main() {

	var wg sync.WaitGroup

	c := sync.NewCond(new(sync.Mutex))
	var printed int

	for i := 0; i &lt; 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&#39;s turn
			for printed != y {
				c.Wait()
			}
			fmt.Printf(&quot;%d &quot;, 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.

huangapple
  • 本文由 发表于 2014年9月3日 01:32:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/25629109.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定