go实现简单的chan

文章目录

    • 借助sync.Cond可以实现简单的chan
    • golang的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执行队列

你可能感兴趣的:(golang,开发语言,后端)