go的协程池实现

Golang 线程 和 协程 的区别

对于进程线程,都是有内核进行调度,有 CPU 时间片的概念,进行抢占式调度(有多种调度算法)

对于协程(用户级线程),这是对内核透明的,也就是系统并不知道有协程的存在,是完全由用户自己的程序进行调度的,因为是由用户程序自己控制,那么就很难像抢占式调度那样做到强制的 CPU 控制权切换到其他进程/线程,通常只能进行协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到。

goroutine 和 协程 区别

本质上,goroutine 就是协程。不同的是,Golang 在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU § 转让出去,让其他 goroutine 能被调度并执行,也就是 Golang 从语言层面支持了协程。Golang 的一大特色就是从语言层面原生支持协程,在函数或者方法前面加 go关键字就可创建一个协程。

  1. 内存消耗方面
      每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少。
      goroutine:2KB
      线程:8MB
  2. 线程和 goroutine 切换调度开销方面
      线程/goroutine 切换开销方面,goroutine 远比线程小
      线程:涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP…等寄存器的刷新等
      goroutine:只有三个寄存器的值修改 - PC / SP / DX

由于协程是非抢占式的调度,无法实现公平的任务调用,也无法直接利用多核优势。因此,我们不能武断地说协程是比线程更高级的技术。

协程池实现

golang中启动一个协程不会消耗太多资源,有人认为可以不用协程池。但是当访问量增大时,可能造成内存消耗完,程序崩溃。写了一个协程池的Demo。

定义接口体:
    Pool : 定义goroutine相关控制参数
    Job:根据应用场景传入需要处理的对象
    Work:加工处理Job对象

package main

import (
   "fmt"
   "time"
)

type Pool struct {
   JobQueue chan Job     //待处理的任务队列
   WorkerCurrentNum int      //当前正在工作的协程数
   MaxWorker int     //允许最大工作协程数
   Result chan int        //处理完成的任务结果队列
   resultNum int     //已处理任务数
}

type Job struct {
   ID int
}

type Worker struct {
   Result chan int
}

func (w *Worker) DoJob(job Job) {
   //fmt.Println(job.ID)
   fmt.Println("worker started  job", job.ID)
   w.Result <- job.ID
   fmt.Println("worker finished  job", job.ID)
}

// 往Job任务队列里面放入待处理的job
func (g *Pool) AddJob(job Job) {
   g.JobQueue <- job
}

func (g *Pool) stop() {
   for {
      c1 := <- g.Result
      fmt.Println("接收job", c1, "已处理结果")
      g.WorkerCurrentNum --
      g.resultNum ++
   }
}

//  开启协程池
func (g *Pool) Run() {
   go g.stop()

   outLoop:
      for  {
         if g.WorkerCurrentNum < g.MaxWorker {
            select {
            //   data, ok := <-ch  非阻塞接收数据
            //  data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
            //   ok:表示是否接收到数据。
            case job, ok := <- g.JobQueue:
               if ok {
                  fmt.Println("Waiting for job...")
                  worker := &Worker{g.Result}
                  go worker.DoJob(job)
                  g.WorkerCurrentNum++
               }else {
                  break outLoop  //JobQueue已经关闭,不需要再创建Worker协程
               }
            }
         }
      }

   //保证JobQueue中18个任务全部被处理时退出run
   for g.resultNum != 18 {
      time.Sleep(time.Second)
   }
}

func main()  {
   jobQueue := make(chan Job)
   resultQueue := make(chan int)

   p := &Pool{
      MaxWorker: 5,
      JobQueue: jobQueue,
      Result: resultQueue,
      resultNum: 0,
   }

   go func() {
      for i:= 0; i < 18; i++{
         job := Job{i}
         p.AddJob(job)
      }
      close(p.JobQueue)
   }()

   p.Run()

   fmt.Println("Complete main")
}

我是pavel,一位憨憨傻傻的程序员,平时幽默又有才,专注于Java,go,微服务,云开发。不定时发送些腾讯程序员的工作/生活日常,请大家多多关注我的公众号!
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
协程是一种将协程资源进行复用的技术,可以有效地提高协程的利用效率和整个应用的性能。在PHP中,可以使用Swoole扩展提供的协程实现。 Swoole提供了一个Swoole\Coroutine\Channel类,该类可以用于实现协程。具体实现过程如下: 1. 创建一个Swoole\Coroutine\Channel对象,用于存储协程资源; 2. 调用Swoole\Coroutine\run()方法创建一个协程,其中每个协程都会从Swoole\Coroutine\Channel对象中获取协程资源,并执行相应的任务; 3. 将任务添加到协程中的协程中,等待协程执行完毕后,将协程资源返回给Swoole\Coroutine\Channel对象,以便下次使用。 下面是一个简单的示例代码: ```php $pool = new Swoole\Coroutine\Channel(10); Swoole\Coroutine\run(function () use ($pool) { for ($i = 0; $i < 10; $i++) { go(function () use ($i, $pool) { // 从协程中获取协程资源 $resource = $pool->pop(); // 执行任务 echo "Coroutine {$i} started\n"; co::sleep(1); echo "Coroutine {$i} finished\n"; // 将协程资源返回给协程 $pool->push($resource); }); } }); ``` 在上面的代码中,我们首先创建了一个Swoole\Coroutine\Channel对象,用于存储协程资源。然后,我们创建了一个包含10个协程协程,并将每个协程添加到中。每个协程都会从协程中获取一个协程资源,并执行相应的任务。任务执行完毕后,将协程资源返回给协程,以便下次使用。 需要注意的是,协程的大小应该根据实际情况进行设置。如果的大小太小,可能会导致协程资源不足;如果的大小太大,可能会导致协程资源的浪费。在实际使用中,可以根据负载情况动态地调整协程的大小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值