进程

线程

协程

设置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 并发编程的更多相关文章

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

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

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

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

  3. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  4. 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport

    在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...

  5. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  6. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  7. 【Java并发编程实战】-----“J.U.C”:Exchanger

    前面介绍了三个同步辅助类:CyclicBarrier.Barrier.Phaser,这篇博客介绍最后一个:Exchanger.JDK API是这样介绍的:可以在对中对元素进行配对和交换的线程的同步点. ...

  8. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  9. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  10. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

随机推荐

  1. Hello,world!一切的开始

    普及知识 当我们准备开发Java程序时,我们需要两样基础的工具--JDK与IDE.在这里需要解释一下什么是JDK还有IDE.JDK的全称是Java Development kit,即Java开发工具集 ...

  2. 前端 JS/TS 调用 ASP.NET Core gRPC-Web

    前言 在上两篇文章中,介绍了ASP.NET Core 中的 gRPC-Web 实现 和 在 Blazor WebAssembly 中使用 gRPC-Web,实现了 Blazor WebAssembly ...

  3. JavaScript(4)---BOM详解

    JavaScript(4)---BOM详解 之前写过一篇有关DOM的博客:JavaScript(2)---DOM详解 DOM有个顶级对象叫:document.同样BOM中也有顶级对象叫 window. ...

  4. 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 ...

  5. javabst1

    (单选题)下列概念中不包括任何实现,与存储空间没有任何关系的是() A)类 B)接口 C)抽象类 D)对象 2.(单选题)HTTP状态码中表示请求资源不存在的是(). A)100 B)200 C)30 ...

  6. 文件传输基础----Java IO流

    编码问题 一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵 ...

  7. JAVA中CLASS.FORNAME的含义

    Class.forName(xxx.xx.xx) 返回的是一个类, .newInstance() 后才创建一个对象 Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的 ...

  8. DBSync如何连接并同步MySQL

    DBSync支持各种异构数据库之间的同步,如Access.SQL Server.Oracle.MySQL.DB2等,但很多用户在同步MySQL时遇到问题,这里讲述一下解决措施. 1.问题现象DBSyn ...

  9. Codeforces_798

    A.暴力把每个位置的字符改成另外25个字符,判断是否回文. #include<bits/stdc++.h> using namespace std; string s; int main( ...

  10. NJUPT_Wrj 个人训练实录

    9暑假了,开个训练实录,记录自己每天的训练以及补题(仅含个人训练,组队训练另开坑)希望能坚持下去QAQ 7.5日常:BZOJ1607线性筛.1601MST.1602LCA.1606背包.1625背包比 ...