Skip to content

Instantly share code, notes, and snippets.

@flisky
Created November 5, 2013 03:25
Show Gist options
  • Save flisky/7313394 to your computer and use it in GitHub Desktop.
Save flisky/7313394 to your computer and use it in GitHub Desktop.
Goroutine By Example
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