golang 并发编程之生产者消费者

golang 最吸引人的地方可能就是并发了,无论代码的编写上,还是性能上面,golang 都有绝对的优势

学习一个语言的并发特性,我喜欢实现一个生产者消费者模型,这个模型非常经典,适用于很多的并发场景,下面我通过这个模型,来简单介绍一下 golang 的并发编程

go 并发语法

协程 go

协程是 golang 并发的最小单元,类似于其他语言的线程,只不过线程的实现借助了操作系统的实现,每次线程的调度都是一次系统调用,需要从用户态切换到内核态,这是一项非常耗时的操作,因此一般的程序里面线程太多会导致大量的性能耗费在线程切换上。而在 golang 内部实现了这种调度,协程在这种调度下面的切换非常的轻量级,成百上千的协程跑在一个 golang 程序里面是很正常的事情

golang 为并发而生,启动一个协程的语法非常简单,使用 go 关键字即可

go func () {
    // do something
}
同步信号 sync.WaitGroup

多个协程之间可以通过 sync.WaitGroup 同步,这个类似于 Linux 里面的信号量

var wg sync.WaitGroup  // 申明一个信号量
wg.Add(1)   // 信号量加一
wg.Done()   // 信号量减一
wg.Wait()   // 信号量为正时阻塞,直到信号量为0时被唤醒
通道 chan

通道可以理解为一个消息队列,生产者往队列里面放,消费者从队列里面取。通道可以使用 close 关闭

ic := make(chan int, 10)  // 申明一个通道
ic <- 10        // 往通道里面放
i := <- ic      // 从通道里面取

close(ic)       // 关闭通道

生产者消费者实现

定义产品类

这个产品类根据具体的业务需求定义

type Product struct {
    name  int
    value int
}
生产者

如果 stop 标志不为 false,不断地往通道里面放 product,完成之后信号量完成

func producer(wg *sync.WaitGroup, products chan<- Product, name int, stop *bool) {
    for !*stop {
        product := Product{name: name, value: rand.Int()}
        products <- product
        fmt.Printf("producer %v produce a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}
消费者

不断地从通道里面取 product,然后作对应的处理,直到通道被关闭,并且 products 里面为空, for 循环才会终止,而这正是我们期望的

func consumer(wg *sync.WaitGroup, products <-chan Product, name int) {
    for product := range products {
        fmt.Printf("consumer %v consume a product: %#v\n", name, product)
        time.Sleep(time.Duration(200+rand.Intn(1000)) * time.Millisecond)
    }
    wg.Done()
}
主线程
var wgp sync.WaitGroup
var wgc sync.WaitGroup
stop := false
products := make(chan Product, 10)

// 创建 5 个生产者和 5 个消费者
for i := 0; i < 5; i++ {
    go producer(&wgp, products, i, &stop)
    go consumer(&wgc, products, i)
    wgp.Add(1)
    wgc.Add(1)
}

time.Sleep(time.Duration(1) * time.Second)
stop = true     // 设置生产者终止信号
wgp.Wait()      // 等待生产者退出
close(products) // 关闭通道
wgc.Wait()      // 等待消费者退出

转载请注明出处
本文链接:http://hatlonely.github.io/2018/03/11/golang-%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85/

生产者消费者模型是一种常见的并发编程模式,用于解决生产者和消费者之间的数据交互问题。该模型包括生产者、消费者和一个缓冲区,生产者将数据放入缓冲区,消费者从缓冲区取出数据进行处理。 以下是两种语言实现生产者消费者模型的例子: 1. Golang实现生产者消费者模型[^1]: ```go package main import ( "fmt" "time" ) func producer(ch chan<- int) { for i := 0; i < 5; i++ { ch <- i fmt.Println("Producer produced:", i) time.Sleep(time.Second) } close(ch) } func consumer(ch <-chan int) { for num := range ch { fmt.Println("Consumer consumed:", num) time.Sleep(time.Millisecond * 500) } } func main() { ch := make(chan int, 3) go producer(ch) consumer(ch) } ``` 2. C语言实现生产者消费者模型[^2]: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0; int out = 0; sem_t empty; sem_t full; pthread_mutex_t mutex; void *producer(void *arg) { int item; for (int i = 0; i < 5; i++) { item = rand() % 100; // 产生随机数作为数据 sem_wait(&empty); pthread_mutex_lock(&mutex); buffer[in] = item; printf("Producer produced: %d\n", item); in = (in + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); sem_post(&full); sleep(1); } pthread_exit(NULL); } void *consumer(void *arg) { int item; for (int i = 0; i < 5; i++) { sem_wait(&full); pthread_mutex_lock(&mutex); item = buffer[out]; printf("Consumer consumed: %d\n", item); out = (out + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); sem_post(&empty); usleep(500000); } pthread_exit(NULL); } int main() { sem_init(&empty, 0, BUFFER_SIZE); sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL); pthread_t producerThread, consumerThread; pthread_create(&producerThread, NULL, producer, NULL); pthread_create(&consumerThread, NULL, consumer, NULL); pthread_join(producerThread, NULL); pthread_join(consumerThread, NULL); sem_destroy(&empty); sem_destroy(&full); pthread_mutex_destroy(&mutex); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值