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 ...
随机推荐
- 扒一扒EOS的前世今生
扒一扒EOS的前世今生 EOS是什么? EOS可以认为是Enterprise Operation System的缩写,即商用的一款分布式区块链操作系统,EOS主要为了解决百万级用户的使用问题,为企 ...
- AngularJS学习之旅—AngularJS 模型(四)
1.AngularJS ng-model 指令 1.ng-model 指令用于绑定应用程序数据到 HTML 控制器(input, select, textarea)的值. 2.ng-model 指令可 ...
- LeetCode算法题-Longest Harmonious Subsequence(Java实现)
这是悦乐书的第270次更新,第284篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第136题(顺位题号是594).我们定义一个和谐数组是一个数组,其最大值和最小值之间的差 ...
- 3星|《HBO的内容战略》:HBO与美国电视业大事记
HBO的内容战略 基本是HBO与美国电视业的大事记.从电视的诞生讲起.HBO在1972年首播,1975年做出一个当时惊世骇俗的决定:勇卫星实时向全美发送信号,随即成为覆盖全国的电视台:80年代受到录像 ...
- 在Linux系统中同步更新我们的Github博客
原理介绍 类似于版本管理,我们把我们的hexo博客文件系统在Github上建立一个分支,通过管理分支提交最新的博客文件系统,保证我们博客框架的更新.然后我们基于最新的博客框架,撰写文章,进行Githu ...
- 0109 ubuntu nginx ssl
1. sudo apt-get install openssl libssl-dev # ./configure --with-http_stub_status_module --with-http_ ...
- springboot+mybatis+pagehelper
springboot+mybatis+pagehelper整合 springboot 版本2.1.2.RELEASE mybatis 版本3.5 pagehelper 版本5.18 支持在map ...
- JetBrains 全套激活 Pycharm Clion 高校学生老师免费用
https://www.jetbrains.com/store/?fromMenu#edition=discounts https://www.jetbrains.com/zh/student/ 用高 ...
- PuTTY 串口调试,为普通用户增加访问串口设备权限
一般情况下,只有 root 用户可以使用 PuTTY 访问串口设备,如果要为普通用户增加访问串口设备的权限,可按如下步骤进行: (以 Ubuntu 14.04.3 系统为例,第一个串口设备,会被识别为 ...
- 图表插件Highcharts的动态化赋值,实现图表数据的动态化设置显示
在很早之前就介绍过图表插件Highcharts的使用了,在2014年的随笔<基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts>,这里基本上都介绍 ...
