当前位置: 首页 > article >正文

go并发编程(中)

目录

一、并发安全性

1.1  变量并发安全性

1.2 容器并发安全性

二、多路复用

三、协程常见的面试题

3.1交替打印奇数偶数


一、并发安全性

1.1  变量并发安全性

这个和C++中并发安全是一样的,主要是多个线程对临界资源的同时访问,最经典的就是 n++操作,因为这一步并不是原子操作的。

这里我们可以用到 atomic(原子操作,让n++变成一步),也可也使用加锁的办法

我们先来模拟一下错误的操作:这里我们开1000个协程,都执行 n++的操作

package main

import (
	"fmt"
	"sync"
)

var n int //n

func main() {
	wg := sync.WaitGroup{}
	wg.Add(1000)
	for i := 0; i < 1000; i++ {
		go func() {
			defer wg.Done()
			n++
		}()
	}
	wg.Wait()
	fmt.Println(n)
}

我们运行一下:

明显可以看到结果不是1000,这就是并发不安全的

1.  使用atomic

2. 用读写锁实现

1.2 容器并发安全性

读写一般的map是不可以的,我们需要用到sync.Map  ,  存数据是 mp.store( i , i ), 读数据是 mp.lode(i)

二、多路复用

我们都知道,当前的IO多路复用有三种: select 、poll、epoll  ,在go语言当中,只有select(这里是针对不同的操作系统进行了封装,我们也不用考虑到底是什么,只管用就行了)

go的select在遍历的时候是只要有一个准备好了就会返回

// 倒计时  模拟火箭发射
func countDown(countCh chan int, n int, finishCh chan struct{}) {
	if n <= 0 { //从n开始倒数
		return
	}
	for {
		countCh <- n //把n放入管道
        time.sleep(time.second)  //等待一秒
		n--          //n减1
		if n <= 0 {  //n减到0时退出
		    finishCh <- struct{}{} //成功结束
			break                  //退出for循环
		}
	}
}

// 中止  键盘有输入就表示要中断
func abort(ch chan struct{}) {
	buffer := make([]byte, 1)
	os.Stdin.Read(buffer) //阻塞式IO,如果标准输入里没数据,该行一直阻塞。注意在键盘上敲完后要按下Enter才会把输入发给Stdin
	ch <- struct{}{}
}

func main() {
	countCh := make(chan int)
	finishCh := make(chan struct{})
	go countDown(countCh, 10, finishCh) //开一个子协程,去往countCh和finishCh里放数据
	abortCh := make(chan struct{})
	go abort(abortCh) //开一个子协程,去往abortCh里放数据

LOOP:
	for { //循环监听
		select { //同时监听3个channel,谁先准备好就执行谁,然后进入下一次for循环
		case n := <-countCh:
			fmt.Println(n)
		case <-finishCh:
			fmt.Println("finish")
			break LOOP //退出for循环。在使用for select时,单独一个break不能退出for循环
		case <-abortCh:
			fmt.Println("abort")
			break LOOP //退出for循环
		}
	}
}

三、协程常见的面试题

3.1交替打印奇数偶数

方法一:使用无缓存的channel 进行通信

// PrintOddAndEven1 /*
func PrintOddAndEven1() {
	//方法一,使用无缓冲的channel进行通信
	var wg = new(sync.WaitGroup) //注意这里需要是指针go语言当中都是值传递
	wg.Add(2)
	ch := make(chan struct{}) //无缓冲channel
	defer close(ch)
	maxVal := 100
	go func() {
		defer wg.Done()
		for i := 1; i <= maxVal; i++ {
			ch <- struct{}{}
			if i%2 == 1 { //奇数
				fmt.Printf("the odd is %d\n", i)

			}
		}
	}()

	go func() {
		defer wg.Done()
		for i := 1; i <= maxVal; i++ {
			<-ch          //从管道当中读取一个数据
			if i%2 == 0 { //偶数
				fmt.Printf("the even is %d\n", i)

			}
		}
	}()
	wg.Wait()

}
func main() {
	PrintOddAndEven1()
	fmt.Println("over")
}

/*

原理因为c1是无缓存的,所以只有当读写同时就绪才不会被阻塞,当同时就绪的时候,两个协程会同时进入if条件语句,他们的i值都是一样的,这时候只有一个是满足条件的,所以会按顺序交替打印出 1~100.

方法二:使用有缓存的channel进行通信

// 方法二:用有缓存的channel进行通信
func HaveCach() {
	wg := sync.WaitGroup{}
	wg.Add(2)

	c1 := make(chan int, 1)
	c2 := make(chan int, 1)

	defer close(c1)
	defer close(c2)
	c1 <- 1 //先往c1当中写入一个数据,确保先打印奇数

	go func() {
		defer wg.Done()
		for i := 1; i < 50; i += 2 {
			<-c1
			fmt.Printf("奇数 :%d\n", i)
			c2 <- 1 //通知c2
		}
	}()
	go func() {
		defer wg.Done()
		for i := 2; i < 50; i += 2 {
			<-c2
			fmt.Printf("偶数 :%d\n", i)
			c1 <- 1 //通知c1
		}
	}()
	wg.Wait()
}

func main() {
	HaveCach()
	fmt.Println("over")
}

第二个方法使用这个有缓冲的channel。有缓冲的channel当容量没有达到上限时写入不会阻塞在这里奇数协程的channel容量为1我们提前给他写入了一个数据因此当偶数和奇数协程都开始读取数据时,首先读取到数据的是奇数协程,奇数协程打印完之后在通知偶数协程打印,偶数协程打印完成之后在通知奇数协程重复下去就实现了交替打印的效果。


http://www.kler.cn/news/155840.html

相关文章:

  • 【计网 面向连接的传输TCP】 中科大笔记 (十 二)
  • 每日一题:LeetCode-209. 长度最小的子数组(滑动窗口)
  • JAVA代码优化:Spring中redis的工具类
  • 计算机视觉(CV)技术的优势和挑战-AI生成版
  • HarmonyOS应用开发者高级认证--96分
  • Nested Named Entity Recognition with Span-level Graphs
  • Linux脚本awk命令
  • SpringBootCache缓存——j2cache
  • docker容器内部文件挂载主机
  • 查看MySQL中具体哪个部分占用了内存
  • 【Python/Java/C++三种语言】20天拿下华为OD笔试之【哈希表】2023B-单词接龙【欧弟算法】全网注释最详细分类最全的华为OD真题题解
  • 纯cpp如何模拟qt的信号与槽
  • 计算UDP报文CRC校验的总结
  • vue2+element-ui npm run build打包后,在服务器打开报错
  • vue 使用decimal.js 解决小数相加合计精确度丢失问题
  • 强化学习------时序差分(Temporal-Difference Learning)
  • 【开源】基于Vue.js的超市账单管理系统的设计和实现
  • Mybatis使用注解实现复杂动态SQL
  • 【CVE-2023-49103】ownCloud graphapi信息泄露漏洞(2023年11月发布)
  • 栈和队列的OJ题--13.用队列实现栈
  • java_基础——ArrayList
  • Spring一些基础问题整理
  • 谱方法学习笔记-下(超详细)
  • 基于Java SSM框架+Vue实现旅游资源网站项目【项目源码+论文说明】计算机毕业设计
  • 【云原生Prometheus篇】Prometheus PromQL语句详解 1.0
  • 使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
  • Redis部署脚本(完成-第一版)
  • shell命令编写
  • 正则表达式从放弃到入门(2):grep命令详解
  • 机器学习---pySpark代码开发