Go语言系列(八)- Goroute和Channel
一、Goroute
1. 进程和线程
A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配合调度的一个独立单位
B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C. 一个进程可以创建和撤销多个线程:同一个进程中的多个线程之间可以并发执行。
2. 并发和并行
A. 多线程程序在一个核的cpu上运行,就是并发
B. 多线程程序在多个核的cpu上运行,就是并行
3. 协程和线程
协程:独立的栈空间,共享栈空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的
线程:一个线程上可以拍多个协程, 协程是轻量级的线程
package main import (
"fmt"
"time"
) func test() {
var i int
for {
fmt.Println(i)
time.Sleep(time.Second)
i++
}
} func main() {
go test()
time.Sleep(time.Second * 10)
// for {
// fmt.Println("i' running in main")
// time.Sleep(time.Second)
// }
}
goroutine
4. goroutine调度模型
5. 如何设置golang运行的cpu核数
package main import (
"fmt"
"sync"
"time"
) type task struct {
n int
} var (
m = make(map[int]uint64)
lock sync.Mutex // 互斥锁
) func calc(t *task) {
var sum uint64
sum = 1
for i := uint64(1); i < uint64(t.n); i++ {
sum *= i
}
fmt.Println(t.n, sum)
lock.Lock()
m[t.n] = sum
lock.Unlock()
} func main() {
for i := 0; i < 16; i++ {
t := &task{n: i}
go calc(t)
}
time.Sleep(time.Second * 10)
lock.Lock()
for k, v := range m {
fmt.Printf("%d! = %v\n", k, v)
}
lock.Unlock()
}
goroute_lock
二、Channel
1.不同的goroutine之间如何进行通讯?
a. 全局变量和锁同步
b. Channel
2. channel概念
a. 类似unix中管道(pipe)
b. 先进先出
c. 线程安全,多个goroutine同时访问,不需要de加锁
d. channel是有类型的,一个整数的channel只能存放整数
3. channel声明
var 变量名 chan 类型
var test chan int
var test chan string
var test chan map[string]string
var test chan stu
var test chan *stu
4. channel初始化
使用make进行初始化,比如:
var test chan int test = make(chan int, 10) var test chan string test = make(chan string, 10)
5. channel基本操作
1. 从channel读取数据
var testChan chan int
testChan = make(chan int, 10)
var a int
a = <- testChan
2. 从channel写入数据
var testChan chan int
testChan = make(chan int, 10)
var a int = 10
testChan <- a
package main import (
"fmt"
"time"
) func write(ch chan int) {
for i := 0; i < 100; i++ {
ch <- i
fmt.Println("put data: ", i)
}
} func read(ch chan int) {
for {
var b int
b = <-ch
fmt.Println(b)
time.Sleep(time.Second)
}
} func main() {
intChan := make(chan int, 10)
go write(intChan)
go read(intChan) time.Sleep(time.Second * 10)
}
goroutine_chan
6.channel阻塞
7. 带缓冲区的channel
1. 如下所示,testChan 只能放一个元素
testChan := make(chan int)
var a int
a = <- testChan
2. 如下所示,testChan是带缓冲区的chan,一次可以放10个元素
testChan = make(chan int, 10)
var a int
testChan <- a
package main import (
"fmt"
) func send(ch chan<- int, exitChan chan struct{}) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
var a struct{}
exitChan <- a
} func recv(ch <-chan int, exitChan chan struct{}) {
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
var a struct{}
exitChan <- a
} func main() {
var ch chan int
ch = make(chan int, 10)
exitChan := make(chan struct{}, 2) go send(ch, exitChan)
go recv(ch, exitChan)
var total = 0
for _ = range exitChan {
total++
if total == 2 {
break
}
}
}
channel_readline
package main import "fmt" type student struct {
name string
} func main() {
var intChan chan int
intChan = make(chan int, 10)
intChan <- 10 var stringChan chan map[string]string
stringChan = make(chan map[string]string, 10)
m := make(map[string]string, 16)
m["stu01"] = ""
m["stu01"] = ""
stringChan <- m var stuChan chan *student
stuChan = make(chan *student, 10)
stu := student{name: "stud01"}
stuChan <- &stu var stuInterChan chan interface{}
stuInterChan = make(chan interface{}, 10)
stu1 := student{name: "stu01"}
stuInterChan <- &stu1 var stu01 interface{}
stu01 = <-stuInterChan fmt.Println(stu01) var stu02 *student
stu02, ok := stu01.(*student)
if !ok {
fmt.Println("can not convert")
return
}
fmt.Println(stu02)
}
channel
8. chan之间的同步
package main import (
"fmt"
) func calc(taskChan chan int, resChan chan int, exitChan chan bool) {
for v := range taskChan {
flag := true
for i := 2; i < v; i++ {
if v%i == 0 {
flag = false
break
}
}
if flag {
resChan <- v
}
}
fmt.Println("exit")
exitChan <- true
} func main() {
intChan := make(chan int, 1000)
resultChan := make(chan int, 1000)
exitChan := make(chan bool, 8) go func() {
for i := 0; i < 100000; i++ {
intChan <- i
}
close(intChan)
}()
for i := 0; i < 8; i++ {
go calc(intChan, resultChan, exitChan)
}
// 等待所有的groutine全部退出
go func() {
for i := 0; i < 8; i++ {
<-exitChan
fmt.Println("wait goroute", i, "exited")
}
close(resultChan)
}()
for v := range resultChan {
fmt.Println(v)
}
}
goroutine_sync
package main import (
"fmt"
) func send(ch chan int, exitChan chan struct{}) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
var a struct{}
exitChan <- a
} func recv(ch chan int, exitChan chan struct{}) {
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
var a struct{}
exitChan <- a
} func main() {
ch := make(chan int, 10)
exitChan := make(chan struct{}, 2)
go send(ch, exitChan)
go recv(ch, exitChan)
var total = 0
for _ = range exitChan {
total++
if total == 2 {
break
}
}
}
goroutine_sync2
9.for range遍历chan
package main import "fmt" func main() {
var ch chan int
ch = make(chan int, 1000) for i := 0; i < 1000; i++ {
ch <- i
} close(ch)
for v := range ch {
fmt.Println(v)
}
}
10. chan的关闭
1. 使用内置函数close进行关闭,chan关闭之后,for range遍历chan中已经放入的元素
2. 使用内置函数close进行关闭, chan关闭之后,没有使用for range的写法,需要判断chan是否关闭。
示例
package main import "fmt" func main() {
var ch chan int
ch = make(chan int, 10) for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
for {
var b int
b, ok := <-ch
if ok == false {
fmt.Println("chan is close")
break
}
fmt.Println(b)
}
}
11. chan的只读和只写
a. 只读chan的声明
var 变量的名字 <-chan int
var readChan <-chan int
b.只写chan的声明
var 变量的名字 chan <- int
var writeChan chan <- int
package main import (
"bufio"
"fmt"
"io"
"os"
) func main() {
file, err := os.Open("test.log")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// 带缓存区的文件读写
reader := bufio.NewReader(file)
var line []byte
for {
data, prefix, err := reader.ReadLine()
if err == io.EOF {
break
}
line = append(line, data...)
if !prefix {
fmt.Printf("data: %s\n", string(data))
line = line[:]
}
}
// fmt.Println(line)
}
带缓存区的读写
12.对chan进行select操作
package main import (
"fmt"
"time"
) func main() {
var ch chan int
ch = make(chan int, 10)
ch2 := make(chan int, 10)
go func() {
var i int
for {
ch <- i
time.Sleep(time.Second)
ch2 <- i * i
time.Sleep(time.Second)
i++
}
}() for {
select {
case v := <-ch:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
case <-time.After(time.Second):
fmt.Println("get data timeout")
time.Sleep(time.Second)
}
// var b int
// b = <-ch
// fmt.Println(b)
}
}
13.定时器的使用
package main import (
"runtime"
"time"
) func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num - 1)
for i := 0; i < 1024; i++ {
go func() {
for {
select {
case <-time.After(time.Microsecond):
// fmt.Println("get data timeout")
}
}
}()
}
time.Sleep(time.Second * 1000)
}
14.一次定时器、超时控制
package main import (
"fmt"
"runtime"
"time"
) func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num - 1)
for i := 0; i < 16; i++ {
go func() {
for {
t := time.NewTicker(time.Second)
select {
case <-time.After(time.Microsecond):
fmt.Println("timeout")
}
t.Stop()
}
}()
}
time.Sleep(time.Second * 1000)
}
15.goroutine中使用recover
package main import (
"fmt"
"runtime"
"time"
) func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("panic:", err)
}
}() var m map[string]int
m["stu"] = 100
} func calc() {
for {
fmt.Println("i'm calc")
time.Sleep(time.Second)
}
} func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num - 1)
go test()
for i := 0; i < 100; i++ {
go calc()
} time.Sleep(time.Second * 1000)
}
三、单元测试
1.文件名必须以_test.go结尾
2. 使用Test开头的函数名作为测试函数
3. 测试案例
package main func add(a, b int) int {
return a + b
} func sub(a, b int) int {
return a - b
}
calc.go
package main import (
"encoding/json"
"io/ioutil"
) type student struct {
Name string
Sex string
Age int
} func (p *student) Save() (err error) {
data, err := json.Marshal(p)
if err != nil {
return
}
err = ioutil.WriteFile("stu.dat", data, 0755)
return
} func (p *student) Load() (err error) {
data, err := ioutil.ReadFile("stu.dat")
if err != nil {
return
}
err = json.Unmarshal(data, p)
return
}
student.go
package main import "testing" func TestAdd(t *testing.T) {
r := add(2, 4)
if r != 6 {
t.Fatalf("add(2,4) error, expect:%d, actual:%d", 6, r)
}
t.Log("test add succ")
}
func TestSub(t *testing.T) {
r := sub(2, 4)
if r != -2 {
t.Fatalf("sub(2,4) error, expect:%d, actual:%d", -2, r)
}
t.Logf("test sub succ")
}
calc_test.go
package main import "testing" func TestSave(t *testing.T) {
stu := &student{
Name: "stu01",
Sex: "man",
Age: 10,
}
err := stu.Save()
if err != nil {
t.Fatalf("save student failed: err%v", err)
}
} func TestLoad(t *testing.T) {
stu := &student{
Name: "stu01",
Sex: "man",
Age: 10,
} err := stu.Save()
if err != nil {
t.Fatalf("save student failed: err%v", err)
}
stu2 := &student{}
err = stu2.Load()
if err != nil {
t.Fatalf("load student failed,err: %v", err)
} if stu.Name != stu2.Name {
t.Fatalf("load student failed, Name not equal")
} if stu.Age != stu2.Age {
t.Fatalf("load student failed, Age not equal")
} if stu.Sex != stu2.Sex {
t.Fatalf("load student failed, Sex not equal")
}
}
student_test.go
package main
main.go
Go语言系列(八)- Goroute和Channel的更多相关文章
- C语言高速入门系列(八)
C语言高速入门系列(八) C语言位运算与文件 本章引言: 在不知不觉中我们的C高速入门系列已经慢慢地接近尾声了,而在这一节中,我们会对 C语言中的位运算和文件进行解析,相信这两章对于一些人来说是陌生的 ...
- Netty 源码解析(八): 回到 Channel 的 register 操作
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第八篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
- Keil MDK STM32系列(八) STM32F4基于HAL的PWM和定时器输出音频
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
- 微软云平台windows azure入门系列八课程
微软云平台windows azure入门系列八课程: Windows Azure入门教学系列 (一): 创建第一个WebRole程序与部署 Windows Azure入门教学系列 (二): 创建第一个 ...
- Java语言基础(八)
Java语言基础(八) 一.数学运算 + - * / % (1)凡是byte short char类型都按int类型的计算 看看上面的代码,为什么出错! 我已经将100转成byte类型,( ...
- SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储
原文:SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft S ...
- Swift语言指南(八)--语言基础之元组
原文:Swift语言指南(八)--语言基础之元组 元组 元组(Tuples)将多个值组合为一个复合值.元组内的值可以是任何类型,各个元素不需要为相同类型(各个元素之间类型独立,互不干扰--Joe.Hu ...
- java多线程系列(八)---CountDownLatch和CyclicBarrie
CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...
- C语言第八讲,指针*
C语言第八讲,指针* 一丶简单理解指针 说到指针,很多人都说是C语言的重点. 也说是C语言的难点. 其实指针并不是难.而是很多人搞不清地址 和 值.以及指针类型. 为什么这样说. 假设有两个变量,如下 ...
- R语言系列:生成数据
R语言系列:生成数据 (2014-05-04 17:41:57) 转载▼ 标签: r语言 教育 分类: 生物信息 生成规则数据1.使用“:“,如x=1:10,注意该方法既可以递增也可以递减,如y=10 ...
随机推荐
- 在搭建tesseract-OCR环境中遇到问题和反省
Tesseract,一款由HP实验室开发由Google维护的开源OCR(Optical Character Recognition , 光学字符识别)引擎,特点是开源,免费,支持多语言,多平台. 在搭 ...
- Java开发学习心得(一):SSM环境搭建
目录 Java开发学习心得(一):SSM环境搭建 1 SSM框架 1.1 Spring Framework 1.2 Spring MVC Java开发学习心得(一):SSM环境搭建 有一点.NET的开 ...
- 一个Web项目中实现多个数据库存储数据并相互切换用过吗?
最近公司一个项目需要连接多个数据库(A和B)操作,根据不同的业务模块查询不同的数据库,因此需要改造下之前的spring-mybatis.xml配置文件以及jdbc.properties配置文件,项目后 ...
- 黑客游戏榜中榜 第一期writeup
[榜中榜 第一期传送门] 注:作者对游戏过程中右键点击进行了限制,下文所提到的"查看源代码",均通过在url头前加上"view-source:"来实现 第一题 ...
- linux上修改mysql登陆密码
1. 修改MySQL的登录设置: # vi /etc/my.cnf 2. 在[mysqld]的段中加上一句:skip-grant-tables 例如: [mysqld] port ...
- MapReduce shuffle过程剖析及调优
MapReduce简介 在Hadoop MapReduce中,框架会确保reduce收到的输入数据是根据key排序过的.数据从Mapper输出到Reducer接收,是一个很复杂的过程,框架处理了所有问 ...
- JavaScript代码组织结构良好的5个特点
JavaScript代码组织结构良好的5个特点,随着JavaScript项目的成长,如果你不小心处理的话,他们往往会变得难以管理.我们发现自己常常陷入的一些问题: 当在创建新的页面时发现,很难重用或测 ...
- 时序数据库InfluxDB:简介及安装
在性能测试过程中,对测试结果以及的实时监控与展示也是很重要的一部分.这篇博客,介绍下linux环境下InfluxDB的安装以及功能特点. 官网地址:influxdata 官方文档:influxdb文档 ...
- JSOI2019 Round2 极限生还
江苏省省队一共13个名额,去掉女生名额, 按1/3校内限制,我们南外只有4个名额, 在noip爆炸(占比35%),省选一轮爆炸(占比40%),(此时蒟蒻在校内排不进前10...) 总算在省选二轮(占比 ...
- tiff图片拆分
本程序下载地址: tiff格式的图片可以由多张图片合成, 也可以拆分为多张图片.不管是合成或者拆分,都借助了第三方开源库Cximage,对于这个图像库的使用,我们没有必要去一行一行的去看它的代码实现, ...