使用goroutine
func echo(r io.Reader, w io.Writer) {
io.Copy(w, r)
}
func main() {
go echo(os.Stdin, os.Stdout)
time.Sleep(30 * time.Second)
fmt.Println("timeout!")
os.Exit(0)
}
使用goroutine闭包
func main() {
fmt.Println("main goroutine.")
// 这里的匿名函数不一定什么时候执行
go func() {
fmt.Println("main.func1 goroutine.")
}()
fmt.Println("main goroutine again.")
// 主动让出时间
runtime.Gosched()
}
等待goroutine
func compress(filename string) error {
in, err := os.Open(filename)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(filename + ".gz")
if err != nil {
return err
}
defer out.Close()
gzout := gzip.NewWriter(out)
_, err = io.Copy(gzout, in)
gzout.Close()
return err
}
func main() {
var wg sync.WaitGroup
var i int = -1
var file string
for i, file = range os.Args[1:] {
// 使用wg来等待goroutine
wg.Add(1)
// 闭包创建链式调用 匿名函数的参数是外部自由变量
// 如果不给参数内部compress将使用file的循环最后的值
// 一个循环中使用goroutine时 要注意goroutine使用的变量
// 是否被for循环改变了 最简单的方法就是循环内复制变量
go func(filename string) {
compress(filename)
wg.Done()
}(file)
}
wg.Wait()
fmt.Printf("Compressed %d files\n", i+1)
}
互斥锁
type words struct {
// 多个goroutine维护一个map 需要对map加锁
// TODU:golang bible chapter 9
sync.Mutex
found map[string]int
}
func newWords() *words {
return &words{found: map[string]int{}}
}
func (w *words) add(word string, n int) {
w.Lock()
defer w.Unlock()
count, ok := w.found[word]
if !ok {
w.found[word] = n
}
w.found[word] = count + n
}
func tallyWords(filename string, dict *words) error {
file, err := os.Open(filename)
if err != nil {
return err
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
word := strings.ToLower(scanner.Text())
dict.add(word, 1)
}
return scanner.Err()
}
func main() {
var wg sync.WaitGroup
w := newWords()
for _, f := range os.Args[1:] {
wg.Add(1)
go func(filename string) {
if err := tallyWords(filename, w); err != nil {
fmt.Println(err.Error())
}
wg.Done()
}(f)
}
wg.Wait()
fmt.Println("words that appear more than once.")
w.Lock()
for word, count := range w.found {
if count > 1 {
fmt.Printf("%s:%d\n", word, count)
}
}
w.Unlock()
}
安全的关闭通道:在接收方从通道内拿到所有数据后发送一个done来通知发送方关闭channel
func worker(msg chan string, done chan bool) {
for {
select {
case <-done:
fmt.Println("Done.")
close(msg)
return
default:
msg<-"hello world."
time.Sleep(500*time.Millisecond)
}
}
}
func main() {
msg := make(chan string)
done := make(chan bool)
timeout := time.After(5 * time.Second)
// 将done传入worker 如果main接收完成通知worker关闭msg
// 如果从worker直接关闭 main会继续接收msg的零值
// 如果在main直接关闭 worker会向关闭的channel发送引起panic
go worker(msg, done)
for {
select {
case m := <-msg:
fmt.Println("recieved msg:", m)
case <-timeout:
done <- true
time.Sleep(500 * time.Millisecond)
return
}
}
}
锁定缓冲通道:将一个通道作为锁来使用
- 函数通过在channel上发送信息获取锁
- 函数继续读写等修改共享内存
- 通过读取channel中的数据释放锁
- 在该锁被释放之前 是一直被占用状态
func worker(id int, lock chan bool) {
fmt.Printf("%d wants the lock.\n", id)
// 谁先向channel发数据这个锁就归谁
lock <- true
fmt.Printf("%d has the lock.\n", id)
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d is releasing the lock.\n", id)
// 从该channel中读取数据意味着释放锁
<-lock
}
func main() {
lock := make(chan bool, 1)
for i := 1; i < 7; i++ {
// 将channel作为锁使用 多个goroutine使用同一个channel
go worker(i, lock)
}
time.Sleep(10 * time.Second)
}
有疑问加站长微信联系(非本文作者)