第八章 Go并发

Go语言区别于其他语言的一大特点就是出色的并发性能,最重要的一个特性那就是go关键字。

并发场景:

  • UI小姐姐一边开着PS软件,一边微信疯狂的和产品经理打字交流,后台还听着网易云音乐。。
  • 双11当天。。大伙疯狂的访问淘宝网站
  • CPU从单核向多核发展,计算机程序不该是串行的,浪费资源
  • 串行程序由于IO操作被阻塞,整个程序处于停滞状态,其他IO无关的任务无法执行

并发必要性:

  • 充分利用CPU核心的优势,提高程序执行效率

实现并发的模型:

  • 多进程,多进程是在操作系统层面并发的基本模式,进程间互不影响,但是开销最大,进程由内核管理。
  • 多线程,属于系统层面的并发模式,也是用的最多的有效模式,大多数软件使用多线程,开销小于多进程。
  • 基于回调的非阻塞/异步IO。此架构处于多线程模式的危机,高并发服务器下,多线程会消耗殆尽服务器的内存和CPU资源。而通过事件驱动的方式使用异步IO,尽可能少用线程,降低开销,Node.js就是如此实践,但是此模式编程复杂度较高。
  • 协程,Coroutine是一种用户态线程,寄存于线程中,系统开销极小,可以有效提高线程任务并发性,使用方式简单,结构清晰,避免多线程的缺点。需要编程语言的支持,如不支持,需要用户自行实现调度器。

共享内存系统是比较常用的并发模式,线程之间通信采用共享内存的方式,程序员需要加锁等操作避免死锁、资源竞争等问题。

计算机科学家又研制出了消息传递系统对线程间共享状态的各种操作被封装在线程之间传递的消息中

Communicating Sequential Processes(顺序通信进程),在CSP系统中,所有的并发操作都是通过独立线程以异步的方式运行,这些线程必须通过再彼此之间发送消息,从而向另一个线程请求信息。

1.1. 进程和线程

进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。

线程是进程的一个执行实例,是比进程更小的独立运行的基本单位。

进程可以创建或销毁多个线程,同一个进程中的多个线程可以并发执行(如百度云盘进程中的,多个下载任务)。

一个程序至少一个进程,一个进程至少一个线程。

“并发”指的是程序的结构,“并行”指的是程序运行时的状态

1.1.1. 并行(parallelism)

这个概念很好理解。所谓并行,就是同时执行的意思,无需过度解读。判断程序是否处于并行的状态,就看同一时刻是否有超过一个“工作单位”在运行就好了。

所以,单线程永远无法达到并行状态。

要达到并行状态,最简单的就是利用多线程和多进程。

但是 Python 的多线程由于存在著名的 GIL,无法让两个线程真正“同时运行”,所以实际上是无法到达并行状态的。

1.1.2. 并发(concurrency)

要理解“并发”这个概念,必须得清楚,并发指的是程序的“结构”。

当我们说这个程序是并发的,实际上,这句话应当表述成“这个程序采用了支持并发的设计”。好,既然并发指的是人为设计的结构,那么怎样的程序结构才叫做支持并发的设计?

正确的并发设计的标准是:使多个操作可以在重叠的时间段内进行(two tasks can start, run, and complete in overlapping time periods)。

1.1.3. 并发和并行

1)多线程程序在单核CPU上运行,就是并发

2)多线程程序在多核CPU上运行,就是并行


为何人们常说提升并发,而不是提升并行

因为并发是通过时间片轮转进行进程调度,是通过技术手段提升并发。

并行是通过硬件提升效率,有钱人可以买一个128核的服务器。


1.2. 协程是什么

执行单位是个抽象的概念,操作系统层面有多个概念与之对应,比如操作系统掌管的进程(process)、进程内的线程(thread)以及进程内的协程(coroutine)

协程在于轻量级,轻松创建百万个而不会导致系统资源衰竭。

多数语言语法层面不直接支持协程,而是通过库的方式支持,然而库的功能也仅仅是线程的创建、销毁与切换,而无法达到协程调用同一个IO操作,如网络通信,文件读写等。

非抢占式多任务处理,由协程主动交出控制权。消耗的资源更少

编译器/解释器/虚拟机层面的多任务,实现的协程调度。

多个协程可能在一个或多个线程上运行。

1.3. 其他语言的协程

c++ Boost.Coroutine
Java不支持
Python 用yield关键字实现协程 3.5之后async def对协程支持,异步函数的定义
golang 不需要定义时区分是否是异步函数 go func(){}

1.4. goroutine

Golang在语言层面支持协程,名为goroutine,Go语言标准库提供所有系统调用操作,都会让出CPU给其他goroutine,使得协程切换管理不依赖于系统的线程和进程,也不依赖于CPU核数。

一个Go进程,可以启动多个goroutine协程。

main函数也是goroutine。

一个普通的机器运行几十个线程负载已经很高了,然而可以轻松创建百万个goroutine。

go标准库的net包,写出的go web server性能直接媲美Nginx。



1.5. goroutine可能的切换点

I/O
select
channel
函数调用
runtime.Gosched()
等待锁

1.6. goroutine入门

第一个goroutine,开启协程,执行函数hello()

package main

import (
"fmt"
"time"
) func hello() {
fmt.Println("hello goroutine")
} func main() {
go hello()
fmt.Println("main thread terminate")
time.Sleep(time.Second)
}

批量开启协程

package main

import (
"fmt"
"time"
) func hello(i int) {
fmt.Println("hello goroutine", i)
} func main() {
//循环开启10个协程,分别执行hello()函数
for i := 0; i < 10; i++ {
go hello(i)
}
time.Sleep(time.Second)
}

编写代码,完成功能

1.在go主进程中,开启goroutine,该协程每秒输出一个你好,我是goroutine

2.在主进程中也每秒输出一个我很好,我是主进程,输出10次后退出程序

3.要求主进程和goroutine同时执行

package main

import (
"fmt"
"strconv"
"time"
) //定义一个协程任务函数
func test() {
for i := 0; i <= 10; i++ {
fmt.Println("你好,我是goroutine" + strconv.Itoa(i))
time.Sleep(time.Second) //睡眠1秒
}
} func main() {
go test()
for i := 0; i <= 10; i++ {
fmt.Println("我很好,我是主进程" + strconv.Itoa(i))
time.Sleep(time.Second) //睡眠1秒
}
}


提示:检测主进程结束,协程也立即结束,或是检测主进程未结束,协程提前退出,可以修改for循环的次数!

1.7. runtime包控制goroutine

runtime.Gosched()让出时间片,如同接力赛跑,让出了接力棒。

gosched如同yield作用,暂停当前的goroutine,放回队列等待下次执行。

package main

import (
"fmt"
"runtime"
) func main() {
go func() {
for i := 0; i < 5; i++ {
fmt.Println("你愁啥")
}
}() for i := 0; i < 2; i++ {
//让出时间片,让其他协程执行
runtime.Gosched()
fmt.Println("尼古拉斯赵四")
}
}

runtime.Goexit()终止当前协程

package main

import (
"fmt"
"runtime"
"time"
) func test() {
defer fmt.Println("ccc")
//return //函数终止,打印a c b 结束
runtime.Goexit() //退出所在协程, 打印 a c 退出主进程
fmt.Println("ddd")
} func main() {
go func() {
fmt.Println("aaa")
test()
fmt.Println("bbb")
}()
//
time.Sleep(time.Second * 3)
}

Go与多核的优势,设置cpu运行数目

go version < 1.8 需要手动设置多核

go version > 1.8 默认用多核,无须设置

package main

import (
"fmt"
"runtime"
) func main() {
cpuNum := runtime.NumCPU()
//可以在这演示下单核时,时间片无切换,仅仅打印数字0的实验 runtime.GOMAXPROCS(1)
runtime.GOMAXPROCS(cpuNum)
for i := 0; i < 500; i++ {
go fmt.Print(1)
fmt.Print(0)
}
}

1.8. goroutine使用recover

package main

import (
"fmt"
"time"
) func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("出错了:", err)
}
}() var m map[string]int
//map必须初始化才能使用
//m=make(map[string]int,10)
m["stu"] = 111 } func calc() {
for {
fmt.Println("我是calc函数")
time.Sleep(time.Second)
}
} func main() {
go test() //协程执行函数,这个函数有错的话,程序会panic退出,做好异常捕捉
for i := 0; i < 2; i++ {
go calc()
}
time.Sleep(time.Second * 10)
}

8.1Go并发的更多相关文章

  1. OAF_开发系列27_实现OAF中Java类型并发程式开发调用XML Publisher(案例)

    20150814 Created By BaoXinjian

  2. .Net多线程编程—并发集合

    并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...

  3. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  4. [高并发]Java高并发编程系列开山篇--线程实现

    Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...

  5. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

  6. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  7. Java多线程基础——对象及变量并发访问

    在开发多线程程序时,如果每个多线程处理的事情都不一样,每个线程都互不相关,这样开发的过程就非常轻松.但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的.这样,一个对象同时被多个线程访问,会出 ...

  8. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  9. 伪共享(false sharing),并发编程无声的性能杀手

    在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...

随机推荐

  1. 防止html标签转义

    function htmlDecode ( str ) { var ele = document.createElement('span'); ele.innerHTML = str; return ...

  2. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  3. (转)对 Linux 专家非常有用的 20 个命令

    谢谢你你给了我们在这篇文章前两个部分的喜欢,美言和支持.在第一部分文章中我们讨论了那些都只是切换到 Linux 和linux新手所需的必要知识的用户的命令. 对 Linux 新手非常有用的 20 个命 ...

  4. Linux Centos7(Mac)安装Docker

    docker 强调隔离性 docker:官网 docker:镜像官网:        镜像官网可以所有应用,选择安装环境:会给出安装命令,例如:docker pull redis 默认拉取最新的版本( ...

  5. Micropython教程之TPYBoard开发板驱动舵机教程(萝卜学科编程教育)

    大家应该都看到过机器人的手臂啊腿脚啊什么的一抽一抽的在动弹吧...是不是和机械舞一样的有节奏,现在很多机器人模型里面的动力器件都是舵机. 但是大家一般见到的动力器件都是像步进电机,直流电机这一类的动力 ...

  6. 腾讯技术工程 | 基于Prophet的时间序列预测

    预测未来永远是一件让人兴奋而又神奇的事.为此,人们研究了许多时间序列预测模型.然而,大部分的时间序列模型都因为预测的问题过于复杂而效果不理想.这是因为时间序列预测不光需要大量的统计知识,更重要的是它需 ...

  7. uiautomatorviewer 出现安卓8.0级以上无法打开的解决方法

    一..本人在使用Android自带的uiautomatorviewer工具来进行app元素定位时,出现了Android 9.0打开不了.出现了如下图错误提示: 经过网上的查阅,总结了几个解决的方法. ...

  8. Flutter 粘合剂CustomScrollView控件

    老孟导读:快乐的51假期结束了,切换为努力模式,今天给大家分享CustomScrollView组件,此组件在以后的项目中会经常用到,CustomScrollView就像一个粘合剂,将多个组件粘合在一起 ...

  9. System.Linq.Dynamic字符串转委托

    以前一直想着有没有一个方法能够把字符串直接转化成函数的,刚好有需求就找了下,还真有. 微软地址:https://docs.microsoft.com/en-us/previous-versions/b ...

  10. Hadoop入门学习笔记-第一天 (HDFS:分布式存储系统简单集群)

    准备工作: 1.安装VMware Workstation Pro 2.新建三个虚拟机,安装centOS7.0 版本不限 配置工作: 1.准备三台服务器(nameNode10.dataNode20.da ...