goroutine之间的同步

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

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

package main

import "strconv"
import "fmt" func main() {
taskChan := make(chan string, 3)
doneChan := make(chan int, 1) for i := 0; i < 3; i++ {
taskChan <- strconv.Itoa(i)
fmt.Println("send: ", i)
} go func() {
for i := 0; i < 3; i++ {
task := <-taskChan
fmt.Println("received: ", task)
}
doneChan <- 1
}() <-doneChan
}
  • 创建一个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! "的错误,真实的代码是下面这样的:

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

我的本意是从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任务再分配出去,等待全部完成。

// Initialize a channel which records the process of the map jobs.
mapJobChannel := make(chan string) // Start a goroutine to send the nMap(the number of the Map jobs) tasks to the main routine.
go func() {
for m := 0; m < nMap; m++ {
// Send the "start a Map job <m>" to the receiver.
mapJobChannel <- "start, " + strconv.Itoa(m)
}
}() // Main routine listens on this mapJobChannel for the Map job task information.
nMapJobs := nMap // Process the nMap Map tasks util they're all done.
for nMapJobs > 0 {
// Receive the Map tasks from the channel.
taskInfo := strings.Split(<-mapJobChannel, ",")
state, mapNo := taskInfo[0], taskInfo[1] if state == "start" {
// Assign a Map task with number mapNo to a worker.
go func() {
// Wait for a worker to finish the Map task.
ok, workNo := assignJobToWorker("Map", mapNo)
if ok {
// Send a task finish signal and set the worker's state to idle.
mapJobChannel <- "end, " + mapNo
setWorkerState(workNo, "idle")
} else {
// Restart this task and set the worker's state to finished.
mapJobChannel <- "start, " + mapNo
setWorkerState(workNo, "finished")
}
}()
} else {
nMapJobs--
}
}

以上是我截取自己写的代码,关于用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. poj 3358

    /** 大意: 给定小数(p/q),求其循环节的大小和循环节开始的位置 解法: 若出现循环 ai*2^m= aj%p; 即 2^m %p =1 若2与p 互素,则可由欧拉函数的, 不互素,需将其转化为 ...

  2. cocos2d-x Android环境搭建

    1.Java虚拟机.分32位和64位.64位: jdk-8u11-windows-x64-8.0.11.12.1406275777 环境变量配置,我的电脑右击->属性->高级系统设置-&g ...

  3. 转: angularjs学习总结(~~很详细的教程)

    1 前言 前端技术的发展是如此之快,各种优秀技术.优秀框架的出现简直让人目不暇接,紧跟时代潮流,学习掌握新知识自然是不敢怠慢. AngularJS是google在维护,其在国外已经十分火热,可是国内的 ...

  4. HDU 2067 小兔的棋盘

    题解:卡特兰数的几何意义,所以答案就是卡特兰数的两倍 #include <cstdio> #include <iostream> using namespace std; #d ...

  5. VS2010/MFC:模态对话框及其弹出过程

    模态对话框及其弹出过程 加法计算器对话框程序大家照着做一遍后,相信对基于对话框的程序有些了解了,有个好的开始对于以后的学习大有裨益.趁热打铁,这一节讲讲什么是模态对话框和非模态对话框,以及模态对话框怎 ...

  6. poj 1088 滑雪(区间dp+记忆化搜索)

    题目链接:http://poj.org/problem?id=1088 思路分析: 1>状态定义:状态dp[i][j]表示在位置map[i][j]可以滑雪的最长区域长度: 2>状态转移方程 ...

  7. Asp.Net MVC4.0 官方教程 入门指南之三--添加一个视图

    Asp.Net MVC4.0 官方教程 入门指南之三--添加一个视图 在本节中,您需要修改HelloWorldController类,从而使用视图模板文件,干净优雅的封装生成返回到客户端浏览器HTML ...

  8. 记一次排查log4net 不输出日志的解决过程

    最近发现log4net 不输出日志了,重点排查几个地方,发现都没有问题. 1.[assembly: log4net.Config.XmlConfigurator(ConfigFile = " ...

  9. android四大组件详解--摘

    Android四大基本组件介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器 ...

  10. iOS开发之第三方登录微博-- 史上最全最新第三方登录微博方式实现

    相关资源地址: 本项目demo地址 :  https://github.com/zhonggaorong/weiboSDKDemo 最新SDK下载:  最新微博SDK 官网注册地址:点击打开链接 最新 ...