go 并发 sync 及哲学家吃面问题
sync.WaitGroup
我们在前面的例子里面,为了保证协程的运行,使用了time.Sleep来保证他的运行,但这是一种很不明智的做法,当我们就一个的时候我们可以写个等待一秒,但是当我们有许多个协程的时候呢?我们需要等待多久?或者说难道一个协程等一秒?不管哪个方案都是一种灾难
WaitGroup会阻塞线程直到一组协程结束,他使用add来衡量这组协程的规模,使用wait来进行阻塞直到计数器为0,使用done来减少计数器的值,因此done应在函数执行完之后再被执行。需要注意的是当add里面的计数器为0,所有被阻塞的线程都会被释放,而当小于0的时候会引发panic
package main
import (
"fmt"
"sync"
)
func print_something(s string, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println(s)
}
func main() {
var wg sync.WaitGroup
list := []string{
"first",
"second",
"third",
"four",
"five",
"six",
"seven",
}
wg.Add(7)
for i, x := range list {
go print_something(fmt.Sprintf("%d:%s", i, x), &wg)
}
wg.Wait()
}
sync.Mutex
go协程的调度是由go来确定的,我们并不知道其具体的顺序,当多个协程竞争一个变量时很有可能会出现data race问题,这个时候得用互斥锁来保证最后结果的正确性
当一个协程使用lock的时候,这个被lock的资源直到他unlock释放之前,除了他其他的协程都不能动
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var msg string
func update(s string, m *sync.Mutex) {
defer wg.Done()
m.Lock()
msg = s
m.Unlock()
}
func main() {
msg = "init"
var mutex sync.Mutex //mutex is a struct
wg.Add(2)
go update("hello,nice to see you", &mutex)
go update("vice versa", &mutex)
wg.Wait()
fmt.Println(msg)
}
//go run ./example.go
//hello,nice to see you
//go run ./example.go
//hello,nice to see you
//go run ./example.go
//vice versa
让我们现在来看看如果没有锁会发生什么。。。
package main
import "testing"
func Test_update(t *testing.T) {
msg = "nihao"
wg.Add(2)
go update("hello")
go update("ohayo")
wg.Wait()
if msg != "ohayo" {
t.Errorf("error value in msg")
}
}
go test -race .
==================
WARNING: DATA RACE
Write at 0x0000012cb9e0 by goroutine 9:
modulename.update()
/Users/td/Documents/blog/example.go:13 +0x6f
modulename.Test_update.func2()
/Users/td/Documents/blog/example_test.go:10 +0x37
Previous write at 0x0000012cb9e0 by goroutine 8:
modulename.update()
/Users/td/Documents/blog/example.go:13 +0x6f
modulename.Test_update.func1()
/Users/td/Documents/blog/example_test.go:9 +0x37
Goroutine 9 (running) created at:
modulename.Test_update()
/Users/td/Documents/blog/example_test.go:10 +0x92
testing.tRunner()
/usr/local/go/src/testing/testing.go:1439 +0x213
testing.(*T).Run.func1()
/usr/local/go/src/testing/testing.go:1486 +0x47
Goroutine 8 (finished) created at:
modulename.Test_update()
/Users/td/Documents/blog/example_test.go:9 +0x86
testing.tRunner()
/usr/local/go/src/testing/testing.go:1439 +0x213
testing.(*T).Run.func1()
/usr/local/go/src/testing/testing.go:1486 +0x47
==================
--- FAIL: Test_update (0.00s)
testing.go:1312: race detected during execution of test
FAIL
FAIL modulename 0.021s
FAIL
dining philosophers
有五个哲学家绕着一张桌子吃面,这种面必须要两把叉子才能吃,现在每人面前有一个盘子,盘子两边各自有一个叉子,也就是说相邻的两个人不可能同时吃饭,现在设计一个程序来让这五个人都吃到饭
Trevor 的原设计方案是创建左右叉子同步锁,用循环来运行协程,除开第一个人,后面每个人的左手叉子是上一个人的右手叉子,而他的右手叉子则是一个新的锁。这样就会导致一个问题:理论上来讲最后一个人的右手叉子应该是第一个人的左手叉子,但是他这种设计方案使得最后一个的右手叉子变成了一个新的独立的锁,叉子数量实际上多了一个!
新方案:给每个叉子编号,跟人同号代表那人左手边的叉子,同时最大只允许四个人拿起左叉子
package main
import (
"fmt"
"sync"
"time"
)
// The Dining Philosophers problem is well known in computer science circles.
// Five philosophers, numbered from 0 through 4, live in a house where the
// table is laid for them; each philosopher has their own place at the table.
// Their only difficulty – besides those of philosophy – is that the dish
// served is a very difficult kind of spaghetti which has to be eaten with
// two forks. There are two forks next to each plate, so that presents no
// difficulty. As a consequence, however, this means that no two neighbours
// may be eating simultaneously.
// constants
const hunger = 3
// variables
var philosophers = []string{"Plato", "Socrates", "Aristotle", "Pascal", "Locke"}
var wg sync.WaitGroup
var sleepTime = 1 * time.Second
var eatTime = 3 * time.Second
var leftlocknumber = 0
func diningProblem(philosopher string, leftfork *sync.Mutex, rightfork *sync.Mutex) {
defer wg.Done()
fmt.Println(philosopher, "is steated")
time.Sleep(sleepTime)
for i := hunger; i > 0; i-- {
fmt.Println(philosopher, "feels so hungry")
time.Sleep(sleepTime)
if leftlocknumber <= 4 {
leftfork.Lock()
leftlocknumber++
fmt.Println(philosopher, "gets the leftfork")
rightfork.Lock()
fmt.Println(philosopher, "gets the rightfork")
fmt.Println(philosopher, "gets all forks needed,then start to eat")
time.Sleep(sleepTime)
rightfork.Unlock()
fmt.Println(philosopher, "puts down rightfork")
leftfork.Unlock()
leftlocknumber--
fmt.Println(philosopher, "puts down leftfork")
} else {
i++
fmt.Printf("too many people!%d have to wait \n", philosopher)
time.Sleep(sleepTime)
}
}
fmt.Println(philosopher, "is satisfied")
time.Sleep(sleepTime)
fmt.Println(philosopher, "leaves")
}
func main() {
fmt.Println("The dining philosopher problem")
fmt.Println("------------------------------")
forks := make([]*sync.Mutex, 5)
for i := 0; i < len(philosophers); i++ {
forks[i] = &sync.Mutex{}
}
wg.Add(len(philosophers))
for i := 0; i < len(philosophers); i++ {
if i == 4 {
go diningProblem(philosophers[i], forks[i], forks[0])
} else {
go diningProblem(philosophers[i], forks[i], forks[i+1])
}
}
wg.Wait()
fmt.Println("the table is empty")
}
运行结果
go run .
The dining philosopher problem
------------------------------
Locke is steated
Plato is steated
Socrates is steated
Aristotle is steated
Pascal is steated
Pascal feels so hungry
Locke feels so hungry
Plato feels so hungry
Socrates feels so hungry
Aristotle feels so hungry
Aristotle gets the leftfork
Aristotle gets the rightfork
Locke gets the leftfork
Locke gets the rightfork
Locke gets all forks needed,then start to eat
Aristotle gets all forks needed,then start to eat
Socrates gets the leftfork
Locke puts down rightfork
Locke puts down leftfork
Locke feels so hungry
Aristotle puts down rightfork
Aristotle puts down leftfork
Aristotle feels so hungry
Pascal gets the leftfork
Pascal gets the rightfork
Pascal gets all forks needed,then start to eat
Plato gets the leftfork
Socrates gets the rightfork
Socrates gets all forks needed,then start to eat
Socrates puts down rightfork
Socrates puts down leftfork
Socrates feels so hungry
Plato gets the rightfork
Plato gets all forks needed,then start to eat
Aristotle gets the leftfork
Pascal puts down rightfork
Pascal puts down leftfork
Pascal feels so hungry
Aristotle gets the rightfork
Aristotle gets all forks needed,then start to eat
Locke gets the leftfork
Aristotle puts down rightfork
Aristotle puts down leftfork
Aristotle feels so hungry
Plato puts down rightfork
Plato puts down leftfork
Plato feels so hungry
Locke gets the rightfork
Locke gets all forks needed,then start to eat
Pascal gets the leftfork
Socrates gets the leftfork
Socrates gets the rightfork
Socrates gets all forks needed,then start to eat
Socrates puts down rightfork
Socrates puts down leftfork
Socrates feels so hungry
Locke puts down rightfork
Locke puts down leftfork
Locke feels so hungry
Pascal gets the rightfork
Pascal gets all forks needed,then start to eat
Plato gets the leftfork
Plato gets the rightfork
Plato gets all forks needed,then start to eat
Aristotle gets the leftfork
Plato puts down rightfork
Plato puts down leftfork
Plato feels so hungry
Socrates gets the leftfork
Locke gets the leftfork
Locke gets the rightfork
Locke gets all forks needed,then start to eat
Pascal puts down rightfork
Pascal puts down leftfork
Pascal feels so hungry
Aristotle gets the rightfork
Aristotle gets all forks needed,then start to eat
Aristotle puts down rightfork
Aristotle puts down leftfork
Aristotle is satisfied
Socrates gets the rightfork
Socrates gets all forks needed,then start to eat
Locke puts down rightfork
Locke puts down leftfork
Locke is satisfied
Plato gets the leftfork
Pascal gets the leftfork
Pascal gets the rightfork
Pascal gets all forks needed,then start to eat
Pascal puts down rightfork
Pascal puts down leftfork
Pascal is satisfied
Aristotle leaves
Socrates puts down rightfork
Socrates puts down leftfork
Socrates is satisfied
Plato gets the rightfork
Plato gets all forks needed,then start to eat
Locke leaves
Plato puts down rightfork
Plato puts down leftfork
Plato is satisfied
Pascal leaves
Socrates leaves
Plato leaves
the table is empty
评论已关闭