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 ...
随机推荐
- web.xml文件介绍
每个javaEE工程中都有web.xml文件,那么它的作用是什么呢?它是每个web.xml工程都必须的吗? 一个web中可以没有web.xml文件,也就是说,web.xml文件并不是web工程必须的. ...
- Sql Server 获取本周周一
SELECT DATEADD(Day,(@i+1)-(DATEPART(Weekday,getdate())+@@DATEFIRST-1)%7,getdate())
- Asp.Net Core 实现谷歌翻译ApI 免费版
由于谷歌翻译官方API是付费版本,本着免费和开源的精神.分享一下用 Net Core 实现谷歌翻译API的代码. 项目引用的Nuget 包: ChakraCore.NET Newtonsoft.Jso ...
- SQL解析在美团的应用
https://tech.meituan.com/SQL_parser_used_in_mtdp.html 数据库作为核心的基础组件,是需要重点保护的对象.任何一个线上的不慎操作,都有可能给数据库带来 ...
- 重写override
不可重写私有方法. 不可重写非静态的方法,虽然编译器不会报错,但是得不到预期的结果. 可以通过重写的形式对父类的功能进行重新定义,比如:对功能进行修改或者进行升级时. class BaseAction ...
- IDEA 相关整理
插件部分 Lombok 日志不定义
- CodeChef Dynamic GCD
嘟嘟嘟vjudge 我今天解决了一个历史遗留问题! 题意:给一棵树,写一个东西,支持一下两种操作: 1.\(x\)到\(y\)的路径上的每一个点的权值加\(d\). 2.求\(x\)到\(y\)路径上 ...
- Linux内核入门到放弃-虚拟文件系统-《深入Linux内核架构》笔记
VFS的任务并不简单.一方面,它用来提供了一种操作文件.目录及其他对象的统一方法.另一方面,它必须能够与各种方法给出的具体文件系统的实现达成妥协,这些实现在具体细节.总体设计方面都有一些不同之处. 文 ...
- Linux内存管理 (11)page引用计数
专题:Linux内存管理专题 关键词:struct page._count._mapcount.PG_locked/PG_referenced/PG_active/PG_dirty等. Linux的内 ...
- EasyUI datagrid formatter 属性
easyui的formatter属性可以帮助我们更加灵活的显示数据库中的数据. 比如,我有一个启用禁用字段,使用数字表示,1表示启用,2表示禁用,展示给客户的时候我当然希望是中文的形式. 只需要写这么 ...
