107.【并发与标准库】(二)

简介: 107.【并发与标准库】

如果我们给每一个方法都执行协程,那么我们需要在主函数处多等等添加个睡眠

为什么需要在后面添加一个休眠?

因为主函数结束,那么所有的线程都最将会被销毁,我们添加休眠的目的就是给线程续命。

package main
import (
  "fmt"
  "time"
)
func showMsg(msg string) {
  /***
  发现打印机的方式- 输入一个停顿一会会
  */
  for i := 0; i < len(msg); i++ {
    fmt.Printf("%c", msg[i])
    time.Sleep(time.Millisecond * 500) //睡眠 100毫秒
  }
}
func main() {
  go showMsg("java")
  go showMsg("golang")
  time.Sleep(time.Second * 5)   //我们需要在这里添加一个睡眠
}
(3).协程原理探究

Go文件的协程实质上就是: 开辟一个新的线程。主函数退出之后,那么线程也就会自动关闭

2.并发编程之通道 channel

Go 提供了一种称为通道的机制,用于在coroutinue(协程)之间共享数据。当您为coroutinue执行并发活动时,需要受coroutinue之间共享资源或数据,通道充当coroutinue之间的管道(通道)并提供一种机制来保护同步交互

需要在声明通道时指定数据类型。我们可以共享内置、命名、结构和引用类型的值和指针。数据在通道上传递: 在任何给定时间只有一个coroutinue可以访问数据项: 因此按照设计不会发生数据竞争

根据数据交互的行为,有两种类型的通道: 无缓冲通道和缓冲通道。无缓冲通道用于执行coroutinue之间的同步通信,而缓冲通道用于执行异步通信无缓冲通道保证在发送和接受发生的瞬间执行两个coroutinue之间的交换。缓冲通道没有这样的保证。

通道由make函数创建,该函数指定 chan 关键字和通道的元素类型。

(1).创建无缓存通道和缓存通道
1. 无缓存 ->同步
 Unbuffed:=make(chan int)  //整形无缓存通道
2. 有缓存 ->异步
buffed:=make(chan int,10)  //整形有缓冲通道
(2).通道的发送和接受
  1. 通道的发送
通道变量名<-xxxx
  1. 通道的接受
xxx:=<-通道变量名
(3).通道的发送和接受特性
  1. 对于同一个通道,发送操作之间是互斥的,接受操作之间也是互斥的
  2. 发送操作和接受操作中对元素值的处理是不可分割的
  3. 发送操作在完全完成之前会被阻塞,接受操作也是如此

如果不添加go 进行协程实现那么会出现: 死锁错误

package main
import (
  "fmt"
  "math/rand"
  "time"
)
var values = make(chan int) //创建一个int类型的无缓存区的通道
func send() {
  rand.Seed(time.Now().UnixNano()) //获取随机数,不加随机种子,每次遍历获取都是重复的一些随机数据
  value := rand.Intn(10)           // 随机取一个10范围之内的数
  fmt.Printf("send:%v\n", value)
  time.Sleep(time.Second * 5)  //阻塞五秒
  values <- value //把值放到通道中
}
func main() {
  defer close(values) //关闭通道
  go send()           //协程执行这个方法 **** 如果不协程执行会报错
  println("wait...")
  value := <-values //取值
  fmt.Printf("receive:%v\n", value)
  println("end...")
}

3.并发编程之WaitGroup实现同步

waitGroup: 就是指所有的线程实现完毕之后,再继续执行。

(1).等待组的步骤
1. 我们需要引入包
import "sync"
2. 定义等待组的全局变量
var wp sync.WaitGroup //定义等待组
3. 在开启协助的下面我们需要向等待组中+1
wp.Add(1) //每次启动一个线程我们就向等待组中添加1
4. 在线程执行的函数最后向等待组-1
defer wp.Done() //每一个线程完成之后我们就在等待组中减少1
5. 在主线程最后进行等待
wp.Wait() //在这里实时等待,直到等待组中的值为-1,终止执行。 比睡眠更加高效
(2).等待组的实列
package main
import (
  "fmt"
  "sync"
)
var wp sync.WaitGroup //定义等待组
func showMsg(i int) {
  defer wp.Done() //每一个线程完成之后我们就在等待组中减少1
  fmt.Printf("%v\n", i)
}
func main() {
  //  开启十个协程
  for i := 0; i < 10; i++ {
    go showMsg(i)
    wp.Add(1) //每次启动一个线程我们就向等待组中添加1
  }
  // 主协程: 假如我们不在主线程这里设置阻塞的话,那么他就会直接关闭。不会等待其他线程执行完毕
  wp.Wait() //在这里实时等待,直到等待组中的值为-1,终止执行。 比睡眠更加高效。
}

输出结果: 直到所有的等待组全部完成即可

(3).等待组的原理

假如我们开启一个线程,就向等待组中元素+1.如果一个等待组中的一个线程完成了,那么就申请向等待组元素-1.

4.并发编程之runtime包

(1).runtime.Gosched()

让出CPU时间片,重新等待安排任务。

只是给一个机会,但是结果不一定。

package main
import (
  "fmt"
  "runtime"
)
func show(msg string) {
  for i := 0; i < 2; i++ {
    fmt.Printf("%v\n", msg)
  }
}
func main() {
  // 子线程
  go show("小弟")
  for i := 0; i < 2; i++ {
    runtime.Gosched() // 我是老大,给你个机会能够超越我  -》只是机会
    println("老大")
  }
  println("end....")
}

结果: 谁先输出不一定看运气

(2).runtime.Goexit()

退出当前协程

package main
import (
  "fmt"
  "runtime"
  "time"
)
func show(msg string) {
  for i := 0; i < 10; i++ {
    if i >= 5 {
      runtime.Goexit() //直接退出这个协程
    }
    fmt.Printf("%v\n", msg)
  }
}
func main() {
  // 子线程
  go show("小弟")
  time.Sleep(time.Second * 1) //阻塞一秒
  println("end....")
}

结果: 执行到第五个,线程直接终止

(3).runtime.GOMAXPROCS(n)

设定执行程序的最大CPU数是多少?

package main
import (
  "fmt"
  "runtime"
  "time"
)
func a() {
  for i := 0; i < 10; i++ {
    fmt.Printf("a:%v\t", i)
  }
}
func b() {
  for i := 0; i < 10; i++ {
    fmt.Printf("b:%v\t", i)
  }
}
func main() {
  fmt.Printf("runtime.numcpu:%v\n", runtime.NumCPU()) //默认是最大CPU进行执行操作
  runtime.GOMAXPROCS(2)       //可以指定CPU的数量
  go a()
  go b()
  time.Sleep(time.Second)
}

5.并发编程之Mutex互斥锁实现同步

除了使用channel实现同步之外,还可以使用Mutex互斥的方式实现同步

(1).Mutex使用步骤
1. 导入包
import "sync"
2. 定义变量
var lock sync.Mutex //定义锁
3. 上锁
lock.Lock() //上锁
4. 解锁
lock.Unlock()  //解锁
(2).Mutex的实列
package main
import (
  "fmt"
  "sync"
  "time"
)
var i int = 100
var wt sync.WaitGroup //设置等待组
var lock sync.Mutex //定义锁
func add() {
  defer wt.Done() //放在这个子线程的最后执行
  time.Sleep(time.Millisecond * 10)
  lock.Lock() //上锁
  i += 1
  fmt.Printf("i++ ->%v\n", i)
  lock.Unlock()  //解锁
}
func sub() {
  defer wt.Done() //放在这个子线程的最后执行
  lock.Lock() //上锁
  i -= 1
  time.Sleep(time.Millisecond * 10) //先等会-因为可能还没有解锁
  fmt.Printf("i-- ->%v\n", i)
  lock.Unlock()  //解锁
}
func main() {
  for i := 0; i < 100; i++ {
    wt.Add(1) //+1
    go add()
    wt.Add(1) //+1
    go sub()
  }
  wt.Wait()
  fmt.Printf("end ->%v", i)
}

输出结果: 要么i++先输出完毕,要么i–先输出完毕

相关文章
|
4月前
|
算法 数据处理 Python
Python并发编程:解密异步IO与多线程
本文将深入探讨Python中的并发编程技术,重点介绍异步IO和多线程两种常见的并发模型。通过对比它们的特点、适用场景和实现方式,帮助读者更好地理解并发编程的核心概念,并掌握在不同场景下选择合适的并发模型的方法。
|
12月前
|
缓存 Go C语言
107.【并发与标准库】(七)
107.【并发与标准库】
45 0
|
4月前
|
数据采集 数据库 C++
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
python并发编程:并发编程中是选择多线程呢?还是多进程呢?还是多协程呢?
46 0
|
12月前
|
缓存 索引
107.【并发与标准库】(九)
107.【并发与标准库】
32 0
|
10月前
|
编译器 Linux 调度
|
15天前
|
消息中间件 存储 安全
python多进程并发编程之互斥锁与进程间的通信
python多进程并发编程之互斥锁与进程间的通信
|
2月前
|
数据采集 算法 数据处理
Python中的并发编程:异步IO与多线程对比分析
传统的多线程编程在Python中因为全局解释器锁(GIL)的存在受到限制,导致多线程并不能充分利用多核处理器的优势。本文将探讨Python中的异步IO编程与多线程编程的差异与优劣,并分析适合的应用场景。
|
3月前
|
并行计算 API Python
Python中的并发编程(1)并发相关概念
Python中的并发编程(1)并发相关概念
|
4月前
|
消息中间件 程序员 调度
Python并发编程:利用多线程提升程序性能
本文探讨了Python中的并发编程技术,重点介绍了如何利用多线程提升程序性能。通过分析多线程的原理和实现方式,以及线程间的通信和同步方法,读者可以了解如何在Python中编写高效的并发程序,提升程序的执行效率和响应速度。
|
4月前
|
消息中间件 安全 调度
基于Python的性能优化(线程、协程、进程)
一、多线程 在CPU不密集、IO密集的任务下,多线程可以一定程度的提升运行效率。