Go并发编程

并发编程

基础

  • 进程(process):进程是并发执行的程序中分配和管理资源的基本单位。
  • 线程(thread):线程是进程的执行单元,是进行调度的实体,是比进程更小的独立运行单位。
  • 并发(concurrent):多线程交替操作同一资源。
  • 并行(parallel):多个线程同时操作多个资源。

协程goroutine

协程的概念

协程是单线程下的并发,又称为微线程。它是实现多任务的另一种方式,只不过是比线程更小的执行单元。因为它自带CPU的上下文,这样只要在合适时机,我们都可以将一个协程切换到另一个协程。英文名Coroutine。

轻量级的线程,独立的栈空间,共享程序堆空间,调度由用户控制,是逻辑态,对资源消耗小。

线程和协程的区别?

线程的切换是一个CPU在不同线程中来回切换,是从系统层面来,不止保存和恢复CPU上下文这么简单,会非常耗费性能。但是协程只是在同一个线程内来回切换不同的函数,只是简单的操作CPU的上下文,所以耗费的性能会大大减小。

多线程并发资源竞争的问题

解决方案一:互斥锁

package main

import (
	"fmt"
	"sync"
	"time"
)

var counter int
var lock sync.Mutex

func increment() {
	lock.Lock()
	for i := 0; i < 100000; i++ {
		counter++
	}
	lock.Unlock()
}

func main() {
	for i := 0; i < 20; i++ {
		go increment()
	}
	time.Sleep(3 * time.Second)
	fmt.Printf("Final counter value: %d\n", counter)
}

解决方案二:channel

chan 本质就是一个数据结构-队列。

先进先出FIFO的规则,线程安全,多goroutine访问不需要加锁,因为通道本身线程安全。

注意:channel是有类型的,定义存放的类型不能放不同的类型。如果是空接口就能放所有类型。

package main

import (
	"fmt"
	"time"
)

var counter int
var incrementChan = make(chan struct{}, 1)

func increment() {
	for i := 0; i < 100000; i++ {
		incrementChan <- struct{}{}
		counter++
		<-incrementChan
	}
}

func main() {
	for i := 0; i < 20; i++ {
		go increment()
	}
	time.Sleep(3 * time.Second)
	fmt.Printf("Final counter value: %d\n", counter)
}

goroutine和channel的综合应用

package main

import (
    "fmt"
    "math/rand"
    "time"
)

var intChan chan int

func WriterData(intCh chan int) {
    rand.Seed(time.Now().UnixNano())
    for i := 1; i < 150; i++ {
       var temp int
       temp = rand.Intn(4) + 10
       intCh <- temp
       fmt.Println("write data:", temp)
    }
    defer close(intCh)
}

func ReadData(intCh chan int, exitChan chan bool) {
    var count int
    for {
       data, ok := <-intCh
       if !ok {
          break
       }
       count++
       fmt.Println("count:", count, "data:", data)
    }
    exitChan <- true
    defer close(exitChan)
}

func main() {
    intChan = make(chan int, 50)
    exitChan := make(chan bool, 1)
    go WriterData(intChan)
    go ReadData(intChan, exitChan)
    time.Sleep(time.Second * 1)
    fmt.Println("main exit")
}

defer匿名函数捕获Panic

多个协程可能会有panic导致整个程序崩溃。

package main

import (
	"fmt"
	"sync"
)

func recoverFromPanic() {
	if r := recover(); r != nil {
		fmt.Println("Recovered from panic:", r)
	}
}

func doWork(id int, wg *sync.WaitGroup) {
	defer wg.Done()
	defer recoverFromPanic()
	fmt.Printf("Worker %d started\n", id)
	if id == 2 {
		panic("Something went wrong!")
	}
	fmt.Printf("Worker %d finished\n", id)
}

func main() {
	var wg sync.WaitGroup
	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go doWork(i, &wg)
	}
	wg.Wait()
	fmt.Println("All workers finished")
}

生产者消费者模式

package main

import (
	"fmt"
	"time"
)

// 模拟订单对象
type OrderInfo struct {
	id int
}

// 生产订单--生产者
func producerOrder(out chan<- OrderInfo) {
	for i := 0; i < 10; i++ {
		order := OrderInfo{id: i + 1}
		fmt.Println("生成订单,订单ID为:", order.id)
		out <- order
	}
	close(out)
}

// 处理订单--消费者
func consumerOrder(in <-chan OrderInfo) {
	for order := range in {
		fmt.Println("读取订单,订单ID为:", order.id)
	}
}

func main() {
	ch := make(chan OrderInfo, 5)
	go producerOrder(ch)
	go consumerOrder(ch)
	time.Sleep(time.Second * 2)
}

协程管道定时任务的应用

1.定时执行某个任务,类似延时消息队列

2.周期性的执行某个任务,类似定期同步某些数据

func main() {
	fmt.Println("当前时间:", time.Now())
	//timer := time.NewTimer(time.Second * 2)
	//t := timer.C
	t := <-time.After(time.Second * 2)
	fmt.Println(t)
}

定时器的停止与重置

timer.Stop()

package main

import (
	"fmt"
	"time"
)

func testChannelTimeout(conn chan int) bool {
	// 设置 1 秒的定时器,若在到了1 s ,则进行打印,说明已经超时
	timer := time.NewTimer(1 * time.Second)

	select {
	case <-conn:
		if timer.Stop() {
			fmt.Println("timer.Stop()")
		}
		return true
	case <-timer.C: // timer 通道超时
		fmt.Println("timer Channel timeout!")
		return false
	}
}

func main() {

	ch := make(chan int, 1)
	// 若打开如下语句,则可以正常关闭定时器
	// 若注释如下语句,则关闭定时器超时
	ch <- 1
	go testChannelTimeout(ch)

	for {
	}
}

timer.Reset()

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("start", time.Now())
	myt := time.NewTimer(time.Second * 2)
	// 重置为5秒
	myt.Reset(time.Second * 5)
	<-myt.C
	fmt.Println("end", time.Now())
}
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

席万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值