go 并发编程
进程
线程
协程
设置golang运行cpu数
1.主线程和协程同时执行
package main import (
"fmt"
"strconv"
"time"
) func test(){
for i:=;i<=;i++{
fmt.Println("test() hello world"+strconv.Itoa(i))
time.Sleep(time.Second)
}
} //开启协程
func main(){
go test()//开启一个协程
for i:=;i<=;i++{
fmt.Println("main() hello,goang"+strconv.Itoa(i))
time.Sleep(time.Second)
} }
//主线程和协程同时执行
2.设置golang运行cpu数
package main import (
"fmt"
"runtime"
) //设置golang运行cpu数
func main(){ //获取当前系统cpu数量
num:=runtime.NumCPU()
//我这里设置num-1的cpu运行go程序
runtime.GOMAXPROCS(num)
fmt.Println("num=",num)
}
3.加锁
package main import (
"fmt"
"sync"//互斥锁
) //求1-n每个数的阶乘
var (
myMap = make(map[int]int, )
lock sync.Mutex
) func test(n int) {
res :=
for i := ; i <= n; i++ {
res *= i
}
//存入map
//加锁
lock.Lock()
myMap[n] = res
lock.Unlock()
} func main() {
for i := ; i <= ; i++ {
go test(i)
}
//输出
//加锁避免还未生成完就进行输出
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}
管道
1.管道说明
package main import "fmt" //管道
func main() {
//1.创建一个存放3个int类型的管道
var intChan chan int
intChan = make(chan int, )
//看看intChan是什么
fmt.Printf("intChan 的值=%v intChan 本身的地址=%p\n", intChan, &intChan) //3.向管道写入数据
intChan <-
num :=
intChan <- num
intChan <-
//intChan<-98//当我们写入数据时,不能超过其容量 //4.看看管道长度和cap(容量)
fmt.Printf("channel len=%v cap=%v\n", len(intChan), cap(intChan))
//5.从管道中读取数据
var num2 int
num2=<-intChan
fmt.Println("num2=",num2)
fmt.Printf("channel len=%v cap=%v\n",len(intChan),cap(intChan))
//6.在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报deadlock
num3:=<-intChan
num4:=<-intChan
num5:=<-intChan
//会报错
fmt.Println("num3=",num3,"num4=",num4,"num5=",num5)
}
2.管道的读写演示
package main import (
"fmt"
) func c1() {
var intChan chan int
intChan = make(chan int, )
intChan <-
intChan <-
intChan <- num1 := <-intChan
num2 := <-intChan
num3 := <-intChan
fmt.Printf("num1=%v num2=%v num3=%v", num1, num2, num3)
} //存放map func c2() {
var mapChan chan map[string]string
mapChan = make(chan map[string]string, )
m1 := make(map[string]string, )
m1["city1"] = "北京"
m1["city2"] = "天津" m2 := make(map[string]string, )
m2["hero1"] = "宋江"
m2["hero2"] = "武松"
mapChan <- m1
mapChan <- m2 } //结构体变量
type Cat struct {
Name string
Age int
} func chanStruct() {
var catChan chan Cat
catChan = make(chan Cat, )
cat1 := Cat{Name: "tom1", Age: }
cat2 := Cat{Name: "tom2", Age: }
catChan <- cat1
catChan <- cat2
fmt.Println(cat1, cat2)
} func chanStruct2() {
var catChan chan *Cat
catChan = make(chan *Cat, ) cat1 := Cat{Name: "tom1", Age: ,}
cat2 := Cat{Name: "tom2", Age: ,}
catChan <- &cat1
catChan <- &cat2
//取出
cat11 := <-catChan
cat22 := <-catChan
fmt.Println(cat11, cat22)
} //存放任意数据类型
func chanOther() {
var allChan chan interface{}
allChan = make(chan interface{}, ) cat1 := Cat{Name: "tom1", Age: ,}
cat2 := Cat{Name: "tom2", Age: ,}
allChan <- cat1
allChan <- cat2
allChan <-
allChan <- "jack" //取出 cat11 := <-allChan
cat22 := <-allChan
v1 := <-allChan
v2 := <-allChan
fmt.Println(cat11, cat22, v1, v2)
} //取出结构体属性需要使用类型断言
func c6() {
var allChan chan interface{}
allChan = make(chan interface{}, ) cat1 := Cat{Name: "tom1", Age: ,}
cat2 := Cat{Name: "tom2", Age: ,}
allChan <- cat1
allChan <- cat2
allChan <-
allChan <- "jack" //取出 cat11 := <-allChan
//使用类型断言,恢复结构体形式
a := cat11.(Cat)
fmt.Println(a.Name)
} //管道的读写演示
func main() {
c6()
}
3.channel遍历以及关闭
package main import "fmt" //channel遍历以及关闭
func chanClose(){ intChan :=make(chan int,)
intChan<-
intChan<-
close(intChan)//关闭
//关闭后,不能继续写入,但可以读取
}
//channel遍历
//1.遍历时如果没有关闭,则会出现deadlock错误
//2.如果已经关闭,遍历完成后,就会退出
func chanFor(){
intChan2:=make(chan int,)
for i:=;i<;i++{
intChan2<-i*
}
//遍历管道不能使用for
close(intChan2)
for v:=range intChan2{
fmt.Println("v=",v)
}
} func main(){ chanFor()
}
4.最佳案例1使用协程
package main import (
"fmt"
"time"
) func writeData(intChan chan int) {
for i := ; i <= ; i++ {
//放入数据
intChan <- i
fmt.Println("writeData", i)
}
close(intChan)
} func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan //接收读取的内容,错误
//取完之后再取,返回false,
//fmt.Printf("布尔值 %v\n",ok)
if !ok {
break
} fmt.Printf("readData 读取到数据=%v\n", v)
}
//标记任务完成
exitChan <- true
close(exitChan)
} func main() {
//创建两个管道
intChan := make(chan int, )
exitChan := make(chan bool, )
//启用协程
go writeData(intChan)
go readData(intChan, exitChan)
time.Sleep(time.Second * ) //1秒
//每次到这里检查是否读完了
for {
_, ok := <-exitChan
if !ok {
fmt.Println("结束了")
break
}
}
}
5.多协程取素数
package main import (
"fmt"
"time"
) //统计1-n素数 //向intChan放入1-n个数
func putNum(intChan chan int) {
for i := ; i <= ; i++ {
intChan <- i
}
close(intChan)
} //取出数据判断是否为素数,如果是就放书primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
//使用for循环
var flag bool
for {
time.Sleep(time.Millisecond * )
num, ok := <-intChan
if !ok {
break
} flag = true
for i := ; i < num; i++ { //不是素数
if num%i == {
flag = false
break
}
}
if flag {
//素数存入
primeChan <- num
}
}
fmt.Println("有一个primeNUm 协程,因为取不到数据,退出")
//标记读取完成
exitChan <- true
}
func main() {
intChan := make(chan int, )
primeChan := make(chan int, )
//标识退出的管道
exitChan := make(chan bool, )
//开启一个协程,向intChan放入1-8000个数
go putNum(intChan)
//开启四个协程,从intChan取出数据,并判断是否为素数,如果是就放入
for i := ; i < ; i++ {
go primeNum(intChan, primeChan, exitChan)
}
//这里主线程 处理
go func() {
for i := ; i < ; i++ {
<-exitChan
}
close(primeChan)
}()
//遍历primeChan,把结果取出
for{
res,ok:=<-primeChan
if !ok{
break
} //输出
fmt.Printf("素数=%d\n",res)
}
fmt.Println("线程退出") }
6.只读,只写
//1.channel可以生名为只读,或者只写性质
func g1(){ //1.可读可写
//var chan1 chan int
//2.声明为只写
var chan2 chan<-int
chan2=make(chan int,)
chan2<-
fmt.Println("chan2=",chan2)
//3.声明为只读
//var chan3 <-chan int
//num2:=<-chan3
}
7.测试只读,只写
package main import (
"fmt"
) //1.channel可以生名为只读,或者只写性质
func g1() { //1.可读可写
//var chan1 chan int
//2.声明为只写
var chan2 chan<- int
chan2 = make(chan int, )
chan2 <-
fmt.Println("chan2=", chan2)
//3.声明为只读
//var chan3 <-chan int
//num2:=<-chan3
} //只写
func send(ch chan<- int, exitchan chan struct{}) {
for i := ; i < ; 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, )
exitChan := make(chan struct{}, )
go send(ch, exitChan)
go recv(ch, exitChan)
var total =
for _ = range exitChan {
total++
if total == {
break
}
}
fmt.Println("结束。。。") }
8.使用select可以解决从通道取数据阻塞问题
package main import (
"fmt"
"time"
) //使用select可以解决从通道取数据阻塞问题 func main(){
//定义一个管道10个数据int
intChan:=make(chan int,)
for i:=;i<;i++{
intChan<- i
}
//2.定义一个管道5个数据string
stringChan:=make(chan string,) for i:=;i<;i++{
stringChan<-"hello"+fmt.Sprintf("%d",i)
}
//传统方法在遍历管道时,如果不关闭会阻塞导致deadlock
//问题,在实际开发中我们不好确定什么关闭管道
//可以使用select方式可以解决
//label:
for{
select{
//注意:这里,如果intChan一直没有关闭,不会一直阻塞而deadlock
//会自动到下一个case匹配
case v :=<-intChan:
fmt.Printf("从intChan读取的数据%d\n",v)
time.Sleep(time.Second)
case v:=<-stringChan:
fmt.Printf("从stringChan读取的数据%v\n",v)
time.Sleep(time.Second)
default:
fmt.Printf("都取不到了,程序可以加入逻辑\n")
time.Sleep(time.Second)
return
//break label
}
}
}
9.goroutine中使用recover,解决协程中出现panic ,导致程序崩溃
package main import (
"fmt"
"time"
) //goroutine中使用recover,解决协程中出现panic ,导致程序崩溃 //
func sayHello() {
for i := ; i < ; i++ {
time.Sleep(time.Second)
fmt.Println("hello world")
}
} func test() {
defer func() {
//捕捉test抛出的panic
if err := recover(); err != nil {
fmt.Println("test() 发生错误", err)
}
}()
//定义一个map
var myMap map[int]string
myMap[] = "golang" } func main() {
go sayHello()
go test()
for i := ; i < ; i++ {
fmt.Println("main() ok=", i)
time.Sleep(time.Second)
}
}
借助scanner阻塞主线程结束
func main(){
go func() {
var times int
for{
times++
fmt.Println("tick",times)
time.Sleep(time.Second)
}
}()
//不输入实际对主协程进行了阻塞
var input string
fmt.Scanln(&input)
}
多个子协程调用顺序是随机的
func printNum() {
for i := ; i < ; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%d", i)
}
} func printLetter() {
for i := 'a'; i < 'e'; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%c", i)
}
}
func main() {
go printNum()
go printLetter()
time.Sleep( * time.Second)
fmt.Println("\n main over") }
生产消费模型
//借助channel实现生产消费模型
func main() {
ch1 := make(chan int)
//生产
go producer(ch1)
//消费
ch_bool1 := make(chan bool)
ch_bool2 := make(chan bool)
ch_bool3 := make(chan bool)
go customer("jack", ch1, ch_bool1)
go customer("rose", ch1, ch_bool2)
go customer("anner", ch1, ch_bool3)
<-ch_bool1
<-ch_bool2
<-ch_bool3
fmt.Println("main over")
} func producer(ch chan int) {
for i := ; i <= ; i++ {
ch <- i
fmt.Printf("生产%d号面包\n", i)
time.Sleep(time.Second)
}
close(ch)
} //消费面包
func customer(name string, ch chan int, ch_bool chan bool) {
for data := range ch {
fmt.Printf("%v吃了%d号面包\n", name, data)
// time.Sleep(time.Second)
}
ch_bool <- true
close(ch_bool)
}
timer
延迟存入通道
func main(){
//创建计时器
//4秒后存入通道
timer1:=time.NewTimer(*time.Second)
fmt.Println(time.Now())
data:=<-timer1.C//取到之前会被阻塞
fmt.Printf("timer_t=%T\n",timer1.C)
fmt.Printf("data=%T\n",data)
fmt.Println("da",data)
}
after延迟存入
//after使用
//延迟存入通道
func main() {
//2.使用After(),返回值<-chan Time,同Timer.C
ch1 := time.After( * time.Second)
fmt.Println(time.Now())
data := <-ch1
fmt.Printf("data_type=%T\n", data)
fmt.Println("data", data)
}
sync
防止主线程先于子goroutine结束
func main() {
var wg sync.WaitGroup
fmt.Printf("%T\n", wg)
wg.Add()
rand.Seed(time.Now().UnixNano())
go printNum(&wg, )
go printNum(&wg, )
go printNum(&wg, )
wg.Wait() //进入阻塞状态
defer fmt.Println("main over")
}
func printNum(wg *sync.WaitGroup, num int) {
for i := ; i <= ; i++ {
pre := strings.Repeat("\t", num-)
fmt.Printf("%s第%d号子goroutine,%d\n", pre, num, i)
time.Sleep(time.Second)
}
wg.Done()
}
互斥锁实现购票
//互斥锁
var tickets int =
var wg sync.WaitGroup
var mutex sync.Mutex func main() {
wg.Add()
go saleTickets("1号窗口", &wg)
go saleTickets("2号窗口", &wg)
go saleTickets("3号窗口", &wg)
go saleTickets("4号窗口", &wg)
wg.Wait()
defer fmt.Println("所有車票售空")
} func saleTickets(name string, wg *sync.WaitGroup) {
defer wg.Done()
for {
//锁定
mutex.Lock()
if tickets > {
time.Sleep( * time.Second)
//获取窗口的编号
num, _ := strconv.Atoi(name[:])
pre := strings.Repeat("--", num)
fmt.Println(pre, name, tickets)
tickets--
} else {
fmt.Printf("%s 结束售票\n", name)
mutex.Unlock()
break
}
//解锁
mutex.Unlock()
}
}
读写互斥锁
//读写互斥锁
func main() {
var rwm sync.RWMutex
for i := ; i <= ; i++ {
go func(i int) {
fmt.Printf("goroutine %d,尝试读锁定。\n", i)
rwm.RLock()
fmt.Printf("goroutine %d 已经锁定了\n", i)
time.Sleep( * time.Second)
fmt.Printf("goroutine %d,读解锁", i)
rwm.RUnlock()
}(i)
} time.Sleep( * time.Second)
fmt.Println("main.,尝试写锁定")
rwm.Lock()
fmt.Println("main 已经锁定了")
rwm.Unlock()
fmt.Println("main 写解锁")
}
解决资源竞争
go 并发编程的更多相关文章
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- 伪共享(false sharing),并发编程无声的性能杀手
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- 【Java并发编程实战】----- AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...
- 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport
在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...
- 【Java并发编程实战】----- AQS(二):获取锁、释放锁
上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...
- 【Java并发编程实战】-----“J.U.C”:CLH队列锁
在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...
- 【Java并发编程实战】-----“J.U.C”:Exchanger
前面介绍了三个同步辅助类:CyclicBarrier.Barrier.Phaser,这篇博客介绍最后一个:Exchanger.JDK API是这样介绍的:可以在对中对元素进行配对和交换的线程的同步点. ...
- 【Java并发编程实战】-----“J.U.C”:CountDownlatch
上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...
- 【Java并发编程实战】-----“J.U.C”:CyclicBarrier
在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...
- 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock
ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...
随机推荐
- Hello,world!一切的开始
普及知识 当我们准备开发Java程序时,我们需要两样基础的工具--JDK与IDE.在这里需要解释一下什么是JDK还有IDE.JDK的全称是Java Development kit,即Java开发工具集 ...
- 前端 JS/TS 调用 ASP.NET Core gRPC-Web
前言 在上两篇文章中,介绍了ASP.NET Core 中的 gRPC-Web 实现 和 在 Blazor WebAssembly 中使用 gRPC-Web,实现了 Blazor WebAssembly ...
- JavaScript(4)---BOM详解
JavaScript(4)---BOM详解 之前写过一篇有关DOM的博客:JavaScript(2)---DOM详解 DOM有个顶级对象叫:document.同样BOM中也有顶级对象叫 window. ...
- LeetCode 304. Range Sum Query 2D - Immutable 二维区域和检索 - 矩阵不可变(C++/Java)
题目: Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper ...
- javabst1
(单选题)下列概念中不包括任何实现,与存储空间没有任何关系的是() A)类 B)接口 C)抽象类 D)对象 2.(单选题)HTTP状态码中表示请求资源不存在的是(). A)100 B)200 C)30 ...
- 文件传输基础----Java IO流
编码问题 一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵 ...
- JAVA中CLASS.FORNAME的含义
Class.forName(xxx.xx.xx) 返回的是一个类, .newInstance() 后才创建一个对象 Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的 ...
- DBSync如何连接并同步MySQL
DBSync支持各种异构数据库之间的同步,如Access.SQL Server.Oracle.MySQL.DB2等,但很多用户在同步MySQL时遇到问题,这里讲述一下解决措施. 1.问题现象DBSyn ...
- Codeforces_798
A.暴力把每个位置的字符改成另外25个字符,判断是否回文. #include<bits/stdc++.h> using namespace std; string s; int main( ...
- NJUPT_Wrj 个人训练实录
9暑假了,开个训练实录,记录自己每天的训练以及补题(仅含个人训练,组队训练另开坑)希望能坚持下去QAQ 7.5日常:BZOJ1607线性筛.1601MST.1602LCA.1606背包.1625背包比 ...