Created
November 5, 2013 03:25
-
-
Save flisky/7313394 to your computer and use it in GitHub Desktop.
Goroutine By Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Goroutine By Example | |
==================== | |
尹吉峰 2013/11/05 | |
Python Thread | |
------------- | |
```python | |
import time | |
import threading | |
def subtask(): | |
print "come into subtask" | |
time.sleep(0.001) | |
print "done after a time sleep" | |
def main(): | |
t = threading.Thread(target=subtask) | |
t.setDaemon(True) // Python waits for non-daemon threads by default | |
t.start() | |
t.join() // wait for subtask done | |
if __name__ == "__main__": | |
main() | |
``` | |
Golang Goroutine | |
---------------- | |
```go | |
package main | |
import ("fmt"; "time") | |
func subtask() { | |
fmt.Println("come into subtask") | |
time.Sleep(time.Millisecond) | |
fmt.Println("done after a time sleep") | |
} | |
func main() { | |
go subtask() // exits without any print --- in general | |
} | |
``` | |
Sync with WaitGroup | |
------------------- | |
```go | |
package main | |
import ("fmt"; "sync"; "time") | |
func subtask(wg *sync.WaitGroup) { | |
... | |
wg.Done() | |
} | |
func main() { | |
var wg sync.WaitGroup | |
wg.Add(1) | |
go subtask(&wg) // wg passed as a POINTER | |
wg.Wait() | |
} | |
``` | |
Sync with Channel | |
----------------- | |
```go | |
package main | |
import ("fmt"; "time") | |
func subtask(done chan<- bool) { | |
... | |
done <- true | |
} | |
func main() { | |
done := make(chan bool) // return a reference to an underlying data structure | |
go subtask(done) // why not &done? perhaps since (*chan bool) cannot specify direction. | |
<-done | |
} | |
``` | |
Channel | |
------- | |
* buffered vs unbuffered | |
* send vs receive | |
Communication | |
------------- | |
```python | |
import time | |
import random | |
import threading | |
glob = 0 | |
def subtask(): | |
time.sleep(random.random() * 0.001) | |
global glob | |
glob += 1 | |
def main(): | |
for i in range(1000): | |
t = threading.Thread(target=subtask) | |
t.start() | |
print glob // nonpredictable output | |
if __name__ == "__main__": | |
main() | |
``` | |
By memory | |
--------------- | |
```python | |
... | |
def subtask(lock): | |
with lock: // __enter__: acquire && __exit__: release | |
... | |
def main(): | |
lock = threading.Lock() | |
for i in range(1000): | |
t = threading.Thread(target=subtask, args=(lock,)) | |
t.start() | |
print glob // 1000 | |
``` | |
By communicating | |
---------------- | |
```go | |
func subtask(ch chan<- int) { | |
ch <- 1 | |
} | |
func main() { | |
ch := make(chan int, 100) // buffered channel --- the pool of resources | |
for i := 0; i < 1000; i++ { | |
go subtask(ch) | |
} | |
glob := 0 | |
for i := 0; i < 1000; i++ { | |
glob += <-ch | |
} | |
fmt.Println(glob) | |
} | |
``` | |
Coroutine in Python (yield + send) | |
---------------------------------- | |
```python | |
import random | |
def subtask(): | |
while True: | |
a = yield random.randint(0, 100) | |
print "come from parent ", a | |
c = subtask() | |
c.next() | |
for i in range(10): | |
sub = c.send(i) | |
print "come from subtask ", sub | |
``` | |
Coroutine in Python (greenlet) | |
------------------------------ | |
```python | |
from greenlet import greenlet | |
def test1(): | |
print 12 | |
gr2.switch() | |
print 34 | |
def test2(): | |
print 56 | |
gr1.switch() | |
print 78 | |
gr1 = greenlet(test1) | |
gr2 = greenlet(test2) | |
gr1.switch() | |
``` | |
Goroutine vs Coroutine | |
---------------------- | |
* Coroutine: cooperative scheduling, tied to a specific process | |
* Goroutine: preemptive scheduling, go to any goroutine that is using a specific channel | |
Concurrency vs Parallel | |
----------------------- | |
* Concurrency: 并发,在逻辑层面上共同执行 | |
* Parallel: 并行,并发执行,执行单元无相互依赖,无需沟通 | |
* Example: http://golang.org/doc/play/sieve.go | |
GOMAXPROCS | |
---------- | |
* default 1 (a single OS thread) | |
* set through env variable and/or runtime.GOMAXPROCS API | |
* goroutine would call runtime.Gosched to yield execution | |
Keyword select | |
-------------- | |
```go | |
var c, c1, c2, c3 chan int | |
var i1, i2 int | |
select { | |
case i1 = <-c1: | |
print("received ", i1, " from c1\n") | |
case c2 <- i2: | |
print("sent ", i2, " to c2\n") | |
case i3, ok := (<-c3): // same as: i3, ok := <-c3 | |
if ok { | |
print("received ", i3, " from c3\n") | |
} else { | |
print("c3 is closed\n") | |
} | |
default: | |
print("no communication\n") | |
} | |
for { // send random sequence of bits to c | |
select { | |
case c <- 0: // note: no statement, no fallthrough, no folding of cases | |
case c <- 1: | |
} | |
} | |
select {} // block forever | |
``` | |
closed channel | |
------------- | |
* `close(ch)` close the channel | |
* sending: `panic: runtime error: send on closed channel` | |
* receive: the zero value | |
* no need to close the channel due to GC | |
* closed channel makes the receiving no more blocking | |
nil channel - block bidirection | |
------------------------------- | |
```go | |
package main | |
import ( | |
"fmt" | |
"time" | |
) | |
func WaitMany(a, b chan bool) { | |
for a != nil || b != nil { | |
select { // closed channel never blocks | |
case <-a: | |
a = nil | |
case <-b: | |
b = nil | |
} | |
} | |
} | |
func main() { | |
a, b := make(chan bool), make(chan bool) | |
t0 := time.Now() | |
go func() { | |
close(a) | |
close(b) | |
}() | |
WaitMany(a, b) | |
fmt.Printf("waited %v for WaitMany\n", time.Since(t0)) | |
} | |
``` | |
Advanced Reference | |
------------ | |
* http://golang.org/ref/mem | |
* http://www.sizeofvoid.net/goroutine-under-the-hood/\ | |
* http://dave.cheney.net/category/golang |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment