go实现简单的chan

借助sync.Cond可以实现简单的chan

为避免内存频繁开辟,队列最佳实现是循环队列(为图方便,这里没有采用)。阅读本文前请了解队列和条件变量的知识

package main

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

type Queue struct {
	queue []string
	cond1 *sync.Cond
	cond2 *sync.Cond
	size  int
}

func NewQueue(size int) *Queue {
	var mux sync.Mutex
	return &Queue{
		cond1: sync.NewCond(&mux),
		cond2: sync.NewCond(&mux),
		size:  size,
	}
}

// producer
func (q *Queue) Enqueue(str string) {
	for {
		q.cond1.L.Lock()
		if len(q.queue) >= q.size {
			q.cond2.Wait()
		}
		if len(q.queue) >= q.size {
			q.cond1.L.Unlock()
			continue
		}
		q.queue = append(q.queue, str)

		q.cond1.L.Unlock()
		q.cond1.Signal()
		break
	}
}

// consumer
func (q *Queue) Dequeue() string {
	str := ""
	for {
		q.cond1.L.Lock()
		if len(q.queue) == 0 {
			q.cond1.Wait()
		}
		// 为防止多个协程接收到条件成立信号,必须判断
		if len(q.queue) == 0 {
			q.cond1.L.Unlock()
			continue
		}
		str = q.queue[0]
		q.queue = q.queue[1:]
		q.cond1.L.Unlock()
		q.cond2.Signal()
		break
	}
	return str
}
func main() {

	q := NewQueue(10)
	go func() {
		for i := 0; i < 1000; i++ {
			q.Enqueue(strconv.Itoa(i))
			fmt.Println("写数据:",i)
		}
	}()
	go func() {
		for {
			time.Sleep(time.Millisecond * 200)
			val := q.Dequeue()
			fmt.Println("读数据:",val)
		}
	}()
	time.Sleep(time.Hour)
}

golang的chan的原理

  1. chan创建在堆中,返回指针
  2. 使用环形队列作为缓存区
  3. 每次操作都要加锁,并更新sendx或recvx(队列的头尾指针)
  4. 缓存满,进入等待队列,让出cpu
  5. 被唤醒后,重新进入G执行队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值