秒懂 Golang 中的 条件变量(sync.Cond)

阅读须知:

本篇文章面向的读者: 已经基本掌握Go中的 协程(goroutine)通道(channel)互斥锁(sync.Mutex)读写锁(sync.RWMutex) 这些知识。如果对这些还不太懂,可以先回去把这几个知识点解决了。

首先理解以下三点再进入正题:

  • Go中的一个协程 可以理解成一个独立的人,多个协程是多个独立的人
  • 多个协程都需要访问的 共享资源(比如共享变量) 可以理解成 多人要用的某种公共社会资源
  • 上锁 其实就是加入到某个共享资源的争抢组中上锁完成 就是从争抢组中被选出,得到了期待的共享资源;解锁 就是退出某个共享资源的争抢组。 

正题:

假如有这样一个现实场景:在一个公园中有一个公共厕所,这个厕所一次只能容纳一个人上厕所,同时这个厕所中有个放卷纸的位置,其一次只能放一卷纸,一卷纸的总长度是 5 米,而每个人上一次厕所需要用掉 1 米的纸。而当一卷纸用完后,公园管理员要负责给厕所加上一卷新纸,以便大家可以继续使用厕所。 那么对于这个单人公共厕所,大家只能排队上厕所,当每个人进到厕所的时候,当然会把厕所门锁好,以便任何人都进不来(包括管理员)。管理员若要进到厕所查看用纸情况并加卷纸,也需要排队(因为插队总是不文明对吧)。

那么怎么用 Golang 去模拟上述场景呢?

首先我们先不用 sync.Cond,看如何实现?那么请看下面这段代码:

package main

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

var 卷纸 int
var m sync.Mutex
var wg sync.WaitGroup

func 上厕所(姓名 string){
	m.Lock()
	defer func(){
		m.Unlock()
		wg.Done()
	}()
	fmt.Printf("%s 进到厕所\t",姓名)
	if 卷纸 >= 1 {  // 进到厕所第一件事是看还有没有纸
		fmt.Printf("正在拉屎中...\n")
		time.Sleep(time.Second)
		卷纸 -= 1
		fmt.Printf("%s 已用完厕所,正在离开\n",姓名)
		return
	} 
	fmt.Printf("发现纸用完了,无奈先离开厕所\n")
}

func 加厕纸(){
	m.Lock()
	defer func(){
		m.Unlock()
		wg.Done()
	}()
	fmt.Printf("公园管理员 进到厕所\t")
	if 卷纸 <= 0 { // 管理员进到厕所是看纸有没有用完
		fmt.Printf("公园管理员 正在加新纸...\n")
		time.Sleep(time.Millisecond*500)
		卷纸 = 5
		fmt.Printf("公园管理员 已加上新厕纸,正在离开\n")
	}else{
		fmt.Printf("发现纸还没用完,先离开厕所\n")
	}
}

func main() {
	卷纸 = 5 // 厕所一开始就准备好了一卷纸,长度5米
	要排队上厕所的人 := [...]string{"老王","小李","老张","小刘","阿明","欣欣","西西","芳芳"}
	for _,谁 :&
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Go语言的channel、sync.WaitGroup和context是三个非常重要的并发编程工具。下面我将对它们进行详细介绍。 ### Channel Go语言的channel是一种在多个goroutine之间进行通信的机制。也可以说,channel是一种数据结构,它可以让一个goroutine向另一个goroutine发送一个值,同时还可以让另一个goroutine从channel接收这个值。在Go语言,使用make函数创建一个channel。例如: ```go ch := make(chan int) ``` 这行代码创建了一个类型为int的channel。可以在goroutine使用ch <- value语句向channel发送一个整数,例如: ```go go func() { ch <- 1 }() ``` 可以使用value := <- ch语句从channel接收一个整数,例如: ```go value := <- ch ``` 这行代码会阻塞,直到有一个整数被发送到这个channel为止。需要注意的是,如果没有接收者,发送操作会一直阻塞,直到有接收者为止;如果没有发送者,接收操作也会一直阻塞,直到有发送者为止。 ### sync.WaitGroup sync.WaitGroup是Go语言的一个同步工具,它可以等待一组goroutine完成工作。在WaitGroup,每个goroutine的工作完成后,都需要调用Done方法。主goroutine可以在Wait方法上阻塞,等待所有的goroutine完成工作。例如: ```go var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { // do some work wg.Done() }() } wg.Wait() ``` 这行代码创建了一个WaitGroup,并且启动了10个goroutine进行工作。每个goroutine完成工作后,都会调用wg.Done方法,主goroutine在wg.Wait上阻塞,等待所有的goroutine完成工作。 ### context context是Go语言的一个用于传递请求范围数据的机制。在一个请求处理,可以使用context携带一些请求数据,同时也可以使用context取消请求处理。例如: ```go func handleRequest(ctx context.Context) { // do some work select { case <-ctx.Done(): // handle cancelation default: // continue working } } ``` 这行代码定义了一个处理请求的函数,该函数接收一个context参数。如果context被取消,处理请求的函数将会停止工作。例如: ```go ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(time.Second) cancel() }() handleRequest(ctx) ``` 这行代码创建了一个带有取消功能的context,并且启动了一个goroutine在1后取消context。handleRequest函数会使用这个context来处理请求,并且如果context被取消,handleRequest函数会立刻停止工作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值