Go语言之并发编程(一)
轻量级线程(goroutine)
在编写socket网络程序时,需要提前准备一个线程池为每一个socket的收发包分配一个线程。开发人员需要在线程数量和CPU数量间建立一个对应关系,以保证每个任务能及时地被分配到CPU上进行处理,同时避免多个任务频繁地在线程间切换执行而损失效率。
虽然,线程池为逻辑编写者提供了线程分配的抽象机制。但是,如果面对随时随地可能发生的并发和线程处理需求,线程池就不是非常直观和方便了。能否有一种机制:使用者分配足够多的任务,系统能自动帮助使用者把任务分配到CPU上,让这些任务尽量并发运作。这种机制在Go语言中被称为goroutine。
goroutine的概念类似于线程,但goroutine由Go程序运行时的调度和管理。Go程序会自动将goroutine中的任务分配给CPU。
创建goroutine
Go程序中使用go关键字为一个函数创建一个goroutine。一个函数可以被创建多个goroutine,一个goroutine必定对应一个函数。为一个普通函数创建goroutine的写法如下:go 函数名(参数列表)。
函数名:要调用的函数名。
参数列表:调用函数需要传入的参数。
- package main
- import (
- "fmt"
- "time"
- )
- func running(id, limit int) {
- var times int
- // 构建一个无限循环,当times与limit相等时才可以跳出循环
- for {
- times++
- fmt.Printf("id: %d tick:%d\n", id, times)
- // 延时1秒
- time.Sleep(time.Second)
- if times == limit {
- break
- }
- }
- }
- func main() {
- // 并发执行程序
- go running(1, 5)
- go running(2, 6)
- // 接受命令行输入, 不做任何事情,这里主要是等待两个协程执行完毕
- var input string
- fmt.Scanln(&input)
- }
运行结果:
- id: 2 tick:1
- id: 1 tick:1
- id: 1 tick:2
- id: 2 tick:2
- id: 2 tick:3
- id: 1 tick:3
- id: 2 tick:4
- id: 1 tick:4
- id: 2 tick:5
- id: 1 tick:5
- id: 2 tick:6
代码执行后,命令行会不断输出tick直到times满足limit跳出循环,goroutine终止。同时可以使用fmt.Scanln接受用户输入,这两个环节可以同时进行。
使用匿名函数创建goroutine
go关键字后也可以为匿名函数或闭包启动goroutine。使用匿名函数或闭包创建goroutine时,除了将函数定义部分写在go的后面之外,还需要加上匿名函数的调用参数,格式如下::
- go func(参数列表){
- 函数体
- }(调用参数列表)
参数列表:函数体内的参数变量列表。
函数体:匿名函数的代码。
调用参数列表:启动goroutine时,需要向匿名函数传递的调用参数。
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- go func(id, limit int) {
- var times int
- for {
- times++
- fmt.Printf("id: %d tick:%d\n", id, times)
- time.Sleep(time.Second)
- if times == limit {
- break
- }
- }
- }(1, 5)
- var input string
- fmt.Scanln(&input)
- }
运行结果:
- id: 1 tick:1
- id: 1 tick:2
- id: 1 tick:3
- id: 1 tick:4
- id: 1 tick:5
因为goroutine在main()函数结束时会一同结束,所以在main函数的末尾都加上fmt.Scanln函数,等待用户输入后才会结束main函数
调整并发的运行性能(GOMAXPROCS)
在Go程序运行时(runtime)实现了一个小型的任务调度器。这套调度器的工作原理类似于操作系统调度线程,Go程序调度器可以高效地将CPU资源分配给每一个任务。传统逻辑中,开发者需要维护线程池中线程与CPU核心数量的对应关系。同样的,Go中也可以通过runtime.GOMAXPROCS()函数做到,格式为:
- runtime.GOMAXPROCS(逻辑CPU数量)
这里的逻辑CPU数量可以有如下几种数值:
- <1:不修改任何数值。
- =1:单核心执行。
- >1:多核并行执行。
一般情况下,可以使用runtime.NumCPU()查询CPU数量,并使用runtime.GOMAXPROCS()函数进行设置,例如:
- runtime.GOMAXPROCS(runtime.NumCPU())
Go1.5版本之前,默认使用的是单核心执行。从Go1.5版本开始,默认执行上面语句以便让代码并发执行,最大效率地利用CPU。GOMAXPROCS同时也是一个环境变量,在应用程序启动前设置环境变量也可以起到相同的作用。
并发和并行
在讲解并发概念时,总会涉及另外一个概念并行。下面让我们来了解并发和并行之间的区别。
- 并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
- 并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。
两个概念的区别是:任务是否同时执行。举个栗子:
- 并发:我们可以在播放音乐的同时浏览网站,如果我们的计算机是单核CPU,那么播放音乐和浏览网站是并发执行的,同一时刻CPU只能处理播放音乐或浏览网站。
- 并行:如果我们的计算机不止一个CPU,那么播放音乐和浏览网站如果在不同的的CPU中执行,那么就是并行的。
GO语言在GOMAXPROCS 数量与任务数量相等时,可以做到并行执行,但一般情况下都是并发执行。
goroutine和coroutine的区别
C#、Lua、Python语言都支持coroutine特性。coroutine与goroutine在名字上类似,都可以将函数或者语句在独立的环境中运行,但是它们之间有两点不同:
- goroutine可能发生并行执行;
- coroutine始终顺序执行。
狭义地说,goroutine可能发生在多线程环境下,goroutine无法控制自己获取高优先度支持;coroutine始终发生在单线程,coroutine程序需要主动交出控制权,宿主才能获得控制权并将控制权交给其他coroutine。
- goroutine间使用channel通信,coroutine使用yield和resume操作。
- goroutine和coroutine的概念和运行机制都是脱胎于早期的操作系统。
coroutine的运行机制属于协作式任务处理,早期的操作系统要求每一个应用必须遵守操作系统的任务处理规则,应用程序在不需要使用CPU时,会主动交出CPU使用权。如果开发者无意间或者故意让应用程序长时间占用CPU,操作系统也无能为力,表现出来的效果就是计算机很容易失去响应或者死机。
goroutine属于抢占式任务处理,已经和现有的多线程和多进程任务处理非常类似。应用程序对CPU的控制最终还需要由操作系统来管理,操作系统如果发现一个应用程序长时间大量地占用CPU,那么用户有权终止这个任务。
Go语言之并发编程(一)的更多相关文章
- Go语言 7 并发编程
文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ Go学习群:415660935 今天我们学习Go语言编程的第七章,并发编程.语言级别 ...
- Go语言之并发编程(三)
Telnet回音服务器 Telnet协议是TCP/IP协议族中的一种.它允许用户(Telnet客户端)通过一个协商过程与一个远程设备进行通信.本例将使用一部分Telnet协议与服务器进行通信. 服务器 ...
- Go语言之并发编程(二)
通道(channel) 单纯地将函数并发执行是没有意义的.函数与函数间需要交换数据才能体现并发执行函数的意义.虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题 ...
- go语言之并发编程 channel
前面介绍了goroutine的用法,如果有多个goroutine的话相互之间是如何传递数据和通信的呢.在C语言或者JAVA中,传输的方法包括共享内存,管道,信号.而在Go语言中,有了更方便的方法,就是 ...
- Go语言之并发编程(四)
同步 Go 程序可以使用通道进行多个 goroutine 间的数据交换,但这仅仅是数据同步中的一种方法.通道内部的实现依然使用了各种锁,因此优雅代码的代价是性能.在某些轻量级的场合,原子访问(atom ...
- go语言之并发编程同步一
前面介绍了采用go语法的并行操作以及channel.既然是并行操作,那么就涉及到数据原子性以及同步的问题.所以在Go里面也需要采用同步的机制. 互斥锁: 由标准库代码包sync中的Mutex结构体类型 ...
- go语言之并发编程 channel(1)
单向channel: 单向通道可分为发送通道和接收通道.但是无论哪一种单向通道,都不应该出现在变量的声明中,假如初始化了这样一个变量 var uselessChan chan <- int =m ...
- go语言入门(10)并发编程
1,概述 1.1并发和并行 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行, ...
- Golang并发编程优势与核心goroutine及注意细节
Go语言为并发编程而内置的上层API基于CSP(communication sequential processes,顺序通信进程)模型.这就意味着显式锁都是可以避免的,比如资源竞争,比如多个进程同时 ...
随机推荐
- tf warning等级
from:http://blog.csdn.net/tsinghuahui/article/details/72938764 tf讨厌的warning 2017-08-03 10:02:52.0990 ...
- 【MFC】获取文件大小的方法
[转载]原文地址:http://blog.csdn.net/coderwu/article/details/5652056 MFC 下可以通过 CFileStatus 获取文件大小. ULONGLON ...
- /usr/local/sbin/arpspoof
/usr/local/sbin/arpspoof arpspoof -t 攻击者ip地址 网关ip地址 稍等系,被攻击者机器的arp的缓存就已经变了.
- [Java]在xp系统下java调用wmic命令获取窗口返回信息无反应(阻塞)的解决方案
背景:本人写了一段java代码,调用cmd命令“wmic ...”来获取系统cpu.mem.handle等资源信息.在win7操作系统下运行没有问题,在xp系统下却发现读取窗口反馈信息时无反应(阻塞) ...
- ubuntu双屏调整分辨率
查看屏幕硬件指标 # xrandr Screen 0: minimum 8 x 8, current 2390 x 768, maximum 32767 x 32767 LVDS1 connected ...
- Redis 优缺点
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANSI C语言编写.遵守B ...
- NOIP2018初赛 解题报告
前言 \(NOIP2018\)初赛已经结束了,接下来就要准备复赛了. 不过,在此之前,还是先为初赛写一篇解题报告吧. 单项选择题 送分题.(虽然我还是做错了)可以考虑将它们全部转化为\(10\)进制, ...
- 高阶函数 -------JavaScript
高阶函数 本文摘要:http://www.liaoxuefeng.com/ JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作 ...
- AngularJS 历经实例
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- 关于Runtime Issues
前言:在使用某移动直播的SDK的时候发现,在Run应用的时候会有紫色的警告(Xcode9 + iOS11) 当时还专门提交了工单,当时对方的回复是,大概意思是不影响使用,后期修复. 今天看视频发现这是 ...