goroutine之间的同步

goroutine是golang中在语言级别实现的轻量级线程,仅仅利用go就能立刻起一个新线程。多线程会引入线程之间的同步问题,经典的同步问题如生产者-消费者问题,在c,java级别需要使用锁、信号量进行共享资源的互斥使用和多线程之间的时序控制,而在golang中有channel作为同步的工具。

1. channel实现两个goroutine之间的通信

  1. package main
  2. import "strconv"
  3. import "fmt"
  4. func main() {
  5. taskChan := make(chan string, 3)
  6. doneChan := make(chan int, 1)
  7. for i := 0; i < 3; i++ {
  8. taskChan <- strconv.Itoa(i)
  9. fmt.Println("send: ", i)
  10. }
  11. go func() {
  12. for i := 0; i < 3; i++ {
  13. task := <-taskChan
  14. fmt.Println("received: ", task)
  15. }
  16. doneChan <- 1
  17. }()
  18. <-doneChan
  19. }
  • 创建一个channel,make(chan TYPE {, NUM}), TYPE指的是channel中传输的数据类型,第二个参数是可选的,指的是channel的容量大小。
  • 向channel传入数据,CHAN <- DATA, CHAN指的是目的channel即收集数据的一方,DATA则是要传的数据。
  • 启动一个goroutine接收main routine向channel发送的数据,go func(){ BODY }()新建一个线程运行一个匿名函数。
  • 从channel读取数据,DATA := <-CHAN,和向channel传入数据相反,在数据输送箭头的右侧的是channel,形象地展现了数据从‘隧道’流出到变量里。
  • 通知主线程任务执行结束,doneChan的作用是为了让main routine等待这个刚起的goroutine结束,这里显示了channel的另一个特性,如果从empty channel中读取数据,则会阻塞当前routine,直到有数据可以读取。

上面这个程序就是main routine向另一个routine发送了3条int类型的数据,当3条数据被接收到后,主线程也从阻塞状态恢复运行,随后结束。

2. 不要陷入“死锁”

我一开始用channel的时候有报过"fatal error: all goroutines are asleep - deadlock! "的错误,真实的代码是下面这样的:

  1. package main
  2. import "fmt"
  3. func main() {
  4. ch := make(chan int)
  5. ch <- 1 // I'm blocked because there is no channel read yet.
  6. fmt.Println("send")
  7. go func() {
  8. <-ch // I will never be called for the main routine is blocked!
  9. fmt.Println("received")
  10. }()
  11. fmt.Println("over")
  12. }

我的本意是从main routine发送给另一个routine一个int型的数据,但是运行出了上述的错误,原因有2个:

  • 当前routine向channel发送/接收数据时,如果另一端没有相应地接收/发送,那么当前routine则会进行休眠。
  • 这个程序的main routine先行在ch <- 1进入休眠状态,程序的余下部分根本来不及运行,那么channel里的数据永远不会被读出,也就不能唤醒main routine,进入“死锁”。

解决这个“死锁”的方法可是是设置channel的容量大小大于1,那么channel就不会因为数据输入而阻塞主程; 或者将数据输入channel的语句置于启动新的goroutine之后。

3. channel作为状态转移的信号源

我跟着MIT的分布式计算课程做了原型为Map-Reduce的课后练习,目的是实现一个Master向Worker分派任务的功能:Master服务器去等待Worker服务器连接注册,Master先将Map任务和Reduce任务分派给这些注册Worker,等待Map任务全部完成,然后将Reduce任务再分配出去,等待全部完成。

  1. // Initialize a channel which records the process of the map jobs.
  2. mapJobChannel := make(chan string)
  3. // Start a goroutine to send the nMap(the number of the Map jobs) tasks to the main routine.
  4. go func() {
  5. for m := 0; m < nMap; m++ {
  6. // Send the "start a Map job <m>" to the receiver.
  7. mapJobChannel <- "start, " + strconv.Itoa(m)
  8. }
  9. }()
  10. // Main routine listens on this mapJobChannel for the Map job task information.
  11. nMapJobs := nMap
  12. // Process the nMap Map tasks util they're all done.
  13. for nMapJobs > 0 {
  14. // Receive the Map tasks from the channel.
  15. taskInfo := strings.Split(<-mapJobChannel, ",")
  16. state, mapNo := taskInfo[0], taskInfo[1]
  17. if state == "start" {
  18. // Assign a Map task with number mapNo to a worker.
  19. go func() {
  20. // Wait for a worker to finish the Map task.
  21. ok, workNo := assignJobToWorker("Map", mapNo)
  22. if ok {
  23. // Send a task finish signal and set the worker's state to idle.
  24. mapJobChannel <- "end, " + mapNo
  25. setWorkerState(workNo, "idle")
  26. } else {
  27. // Restart this task and set the worker's state to finished.
  28. mapJobChannel <- "start, " + mapNo
  29. setWorkerState(workNo, "finished")
  30. }
  31. }()
  32. } else {
  33. nMapJobs--
  34. }
  35. }

以上是我截取自己写的代码,关于用channel来传递当前Map任务的进度信息,用类似信号的方式标注当前的任务执行状态。

  • 当从channel中读取到"start, {NUM}"时找一个空闲的Worker去执行Map任务,并且等待它的完成,完成成功则向channel中发送"end, {NUM}"信号,表明任务完成,如果失败,就重发"start, {NUM}"信号。
  • 从channel中读取到"end, {NUM}"时,把剩余任务数减1。

    这种信号触发的方式,触发Master的状态转移,并且可以通过增加信号以及信号处理的方式,拓展业务处理的情况,暂时还能处理这个需求情景。

MIT分布式系统课程

golang channel初次接触的更多相关文章

  1. 初次接触json...

    这两天发现很多网站显示图片版块都用了瀑布流模式布局的:随着页面滚动条向下滚动,这种布局还会不断加载数据并附加至当前尾部.身为一个菜鸟级的程序员,而且以后可能会经常与网站打交道,我觉得我还是很有必要去尝 ...

  2. 初次接触GWT,知识点总括

    初次接触GWT,知识点概括 前言 本人最近开始研究 GWT(Google Web Toolkit) ,现将个人的一点心得贴出来,希望对刚开始接触 GWT的程序员们有所帮助,也欢迎讨论,共同进步. 先说 ...

  3. [Docker]初次接触

    Docker 初次接触 近期看了不少docker介绍性文章,也听了不少公开课,于是今天去官网逛了逛,发现了一个交互式的小教程于是决定跟着学习下. 仅仅是把认为重点的知识记录下来,不是非常系统的学习和笔 ...

  4. 初次接触:DirectDraw

    第六章 初次接触:DirectDraw 本章,你将初次接触DirectX中最重要的组件:DirectDraw.DirectDraw可能是DirectX中最强大的技术,因为其贯穿着2D图形绘制同时其帧缓 ...

  5. golang channel的使用以及调度原理

    golang channel的使用以及调度原理 为了并发的goroutines之间的通讯,golang使用了管道channel. 可以通过一个goroutines向channel发送数据,然后从另一个 ...

  6. 初次接触scrapy框架

    初次接触这个框架,先订个小目标,抓取QQ首页,然后存入记事本. 安装框架(http://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/install.html) ...

  7. javaweb中的乱码问题(初次接触时写)

    javaweb中的乱码问题 在初次接触javaweb中就遇到了乱码问题,下面是我遇到这些问题的解决办法 1. 页面乱码(jsp) 1. 在页面最前方加上 <%@ page language=&q ...

  8. 初次接触Java

    今天初次接触Eclipse,学着用他来建立java工程,话不多说,来看看今天的成果! 熟悉自己手中的开发工具,热热身 刚上手别慌,有问题找度娘 刚刚拿到这个软件的安装包我是一脸懵逼的,因为是从官网下载 ...

  9. -1.记libgdx初次接触

    学习一门技术最难的是开发环境变量配置和工具配置,以下为我初次接触libgdx时遇到的问题 几个难点记录下 gradle 直接用下到本地,然后放到d盘,链接到就行(gradle-wrapper.prop ...

随机推荐

  1. Android NDK开发(1)----- Java与C互相调用实例详解

    链接地址:http://www.cnblogs.com/lknlfy/archive/2012/03/13/2394153.html 一.概述 对于大部分应用开发者来说可能都不怎么接触到NDK,但如果 ...

  2. POJ 3974 Palindrome(最长回文子串)

    题目链接:http://poj.org/problem?id=3974 题意:求一给定字符串最长回文子串的长度 思路:直接套模板manacher算法 code: #include <cstdio ...

  3. AWT和Swing

    布局分类 一.流式布局 二.边界布局 三.网格布局 四.卡片布局 五.坐标式布局 随意布置控件位置. 六.混合布局

  4. spss

    编辑 SPSS(Statistical Product and Service Solutions),“统计产品与服务解决方案”软件.最初软件全称为“社会科学统计软件包” (SolutionsStat ...

  5. 转:js不同类型对象的比较规则

    Type(x) Type(y) Result type(x)==type(y) x===y otherwise... false null undefined true undefined null ...

  6. JAVA GUI学习 - 窗口【x】按钮关闭事件触发器:重写processWindowEvent(WindowEvent e)方法

    public class WindowListenerKnow extends JFrame { public WindowListenerKnow() { this.setBounds(300, 1 ...

  7. 初试PL/SQL并行编程

    -----------------------------Cryking原创------------------------------ -----------------------转载请注明出处, ...

  8. hdu 4739 Zhuge Liang's Mines (简单dfs)

    Zhuge Liang's Mines Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  9. Python经常使用第三方工具、库、骨架

    Python ImagingLibrary(PIL):它提供强大的图形处理的能力,并提供广泛的图形文件格式支持.该库能进行图形格式的转换.打印和显示.还能进行一些图形效果的处理,如图形的放大.缩小和旋 ...

  10. UILabel显示html文本

    NSString * htmlString = @"<html><body> Some html string \n <font size=\"13\ ...