Go百万级别并发实践

前言

Golang和传统基于 OS 线程和进程实现不同,Go语言的并发是基于用户态的并发,这种并发方式就变得非常轻量,能够轻松运行几万并发逻辑。

Go 的并发属于 CSP 并发模型的一种实现,CSP 并发模型的核心概念是:“不要通过共享内存来通信,而应该通
过通信来共享内存”。这在 Go 语言中的实现就是 Goroutine 和 Channel。

线程池

  • job是任务请求
  • worker是线程
  • worker pool是线程池
    在这里插入图片描述

代码实现

Job.Go

任务接口

// Job接口 含一个do方法,实现该接口,即为一个请求任务
type Job interface {
	Do()
}

worker.go

线程

// 工作线程结构,里面是一个管道,放多个请求任务
type Worker struct {
	JobQueue chan Job
}

// 类似构造函数,返回一个工作线程
func NewWorker() Worker {
	return Worker{JobQueue: make(chan Job)}
}

// 工作线程的成员函数,处理请求
// 形参wq是一个工作队列
// 调用之后,会起一个线程,无限循环,取出任务,放到线程池中  ???
func (w Worker) Run(wq chan chan Job) {
	go func() {
		for {
			wq <- w.JobQueue
			select {
			case job := <-w.JobQueue:
				job.Do()
			}
		}
	}()
}

workerpool.go

线程池

// 线程池
// 线程数、任务队列、装任务队列的管道
type WorkerPool struct {
	workerlen   int
	JobQueue    chan Job
	WorkerQueue chan chan Job
}

// 线程池的构造函数
func NewWorkerPool(workerlen int) *WorkerPool {
	return &WorkerPool{
		workerlen:   workerlen,
		JobQueue:    make(chan Job),
		WorkerQueue: make(chan chan Job, workerlen),
	}
}

// 线程池的成员函数
// 先打印初始化
// 根据工人池的长度,循环构造n个工作线程,然后调用工作线程的Run函数,即:把任务放到线程池中,然后处理这个线程的任务请求
// 开一个协程,一直循环,任务队列中取出任务,如果有,则看工作队列是否有空余线程,如果有,则工作队列中,取出一个工作协程,处理job请求
func (wp *WorkerPool) Run() {
	fmt.Println("初始化worker")

	// 初始化工作线程
	for i := 0; i < wp.workerlen; i++ {
		worker := NewWorker()
		worker.Run(wp.WorkerQueue)
	}

	// 循环获取可⽤用的工作线程
	go func() {
		for {
			select {
			case job := <-wp.JobQueue:  // 管道取出任务
				worker := <-wp.WorkerQueue // 取出空余线程
				worker <- job  // 处理任务
			}
		}
	}()
}

main.go

主函数

package main

import (
	"fmt"
	"runtime"
	"time"
)

// 分数结构——打印显示用
type Score struct {
	Num int
}

// 分数结构实现了Job接口————即打印自己 然后睡眠2s,即有一个score的请求任务,需要打印自己
func (s *Score) Do() {
	fmt.Println("num:", s.Num)
	time.Sleep(time.Second)
}

func main() {
	//num := 100 * 100 * 20
	num := 1
	// debug.SetMaxThreads(1000) //设置最⼤大线程数
	// 注册⼯工作池,传⼊入任务
	// 参数1 worker并发个数

	// 长度为20w的工作池
	p := NewWorkerPool(num)

	// 先运行 挂起
	p.Run()

	// 创建一亿个分数对象,然后丢进工作池的管道
	//datanum := 100 * 100 * 100 * 100
	datanum := 1
	go func() {
		for i := 1; i <= datanum; i++ {
			sc := &Score{Num: i}
			p.JobQueue <- sc
		}
	}()

	// 每隔2秒打印目前的协程数
	for {
		fmt.Println("runtime.NumGoroutine() :", runtime.NumGoroutine())
		time.Sleep(2 * time.Second)
	}
}

发布了41 篇原创文章 · 获赞 26 · 访问量 8088

猜你喜欢

转载自blog.csdn.net/weixin_44879611/article/details/104950858