go 写个协程池 慎入写的不好

##思路讲解

  1. 开辟几个固定的协程可供调度,这个大小也是可以指定的
  2. channel的作用,用于唤醒协程处理并发关系
  3. 协程执行的函数结构的存储问题

###threadPool的数据结构

type ThreadPool struct {
	ch chan int /*channel*/
	size int  
	threadNum int
	tasks *list.List /*list容器,需要这样定义变量*/
}

###函数的保存
task函数需要保存在list当中,但是list我第一次用感觉还是很奇葩的。我看了很多关于list的帖子感觉没讲明白。
####List的使用
go的list的封装很有趣,list里面包含了两部分内容

  1. list.Element 对于存储的数据结构封装(接口)可以看到其实就是个接口罢了,很方便。所有获取元素的方法,比如Front之类的返回的都是Element类型的,所以我们需要调用Value来获得实际上的存储内容。
//container/list––––––––––––––container/list_
type Element struct {
	Value interface{} // 存储在该元素中的值
}
  1. list.List 存储方式 (链表本身)
    定义一个list的方法,第二种适合在结构体里面使用
myList := list.New()
var myList list.List

####使用反射技术存储函数和参数
这里主要是参考这篇文章

//我们就是用这个结构体存储函数的,要是c++11的话就是std::function
type CallAble struct {
	 f reflect.Value 	//存储函数
	 args []reflect.Value //存储参数
} 
//我记得c++11里面也有arg...的用法,不过是用模板,太麻烦
func (this* ThreadPool) Add(f interface{},args... interface{}) {
	var task CallAble ;
	//保存函数指针??
	task.f = reflect.ValueOf( f );
	t := task.f.Type() //判断一下存储的是不是函数
	if t.Kind() == reflect.Func {
		task.args = make([]reflect.Value , len(args) )
		for i := 0 ; i < len(args) ; i ++ {
		    //保存函数的参数
			task.args[i] = reflect.ValueOf(args[i])
		}
		this.tasks.PushBack(task)
	}
}

//函数调用
//截取片段 ——————————————————————————————
if(this.tasks.Len() != 0) {
	e := this.tasks.Front() //返回的是element
	task ,_:= e.Value.(CallAble) //获得element里面存储的callable
	task.f.Call(task.args) // 调用函数
	this.tasks.Remove(e)
}

###全部代码

package ThreadPool

import (
	"fmt"
	"reflect"
	"container/list"
)

type ThreadPool struct {
	ch chan int
	size int
	threadNum int
	tasks *list.List
}

type CallAble struct {
	 f reflect.Value 	//存储函数
	 args []reflect.Value //存储参数
} 

//线程池就是池子里面有几个进程,当进程不够的时候自己去开辟新的线程
//如果执行完毕之后再把这个线程清掉

//Init 函数
func (this* ThreadPool) Init(size int) {
	this.ch = make(chan int)
	this.size = size
	this.tasks = list.New()
	this.threadNum = 0;
	//先初始化几个process
	for i := 0 ; i < size ; i++ {
		go this.process()
	}
}



func (this* ThreadPool) Add(f interface{},args... interface{}) {
	var task CallAble ;
	task.f = reflect.ValueOf( f );
	t := task.f.Type() 
	if t.Kind() == reflect.Func {
		task.args = make([]reflect.Value , len(args) )
		for i := 0 ; i < len(args) ; i ++ {

			task.args[i] = reflect.ValueOf(args[i])
		}
		this.tasks.PushBack(task)
	}
	//每次添加一个task判断一下是否需要开新的task
	//不过应该不需要,因为chan封装了队列,只是个人尝试
	if(this.threadNum >= this.size) {
		go this.process()
	}else {
	    //一定要在协程里面使用chan,我老把主线程锁掉
		go func(){
			this.ch <- 1
		}()
	}
}

func (this* ThreadPool) process() {
	for {
		<- this.ch
		fmt.Printf("threadNum is %d \n" ,this.threadNum)
		this.threadNum ++ //当前进程在忙
	    
		if(this.tasks.Len() != 0) {
			e := this.tasks.Front()
			task ,_:= e.Value.(CallAble)
			task.f.Call(task.args)
			this.tasks.Remove(e)
		}
		//如果当前的的routine数量多了就退出
		if(this.threadNum >= this.size - 1){
			fmt.Println("exit \n" ,this.threadNum)
			this.threadNum --
			break
		} else {
			this.threadNum --//又空闲了一个进程
		}
    }
} 


不写不知道,一写吓一跳,感觉就是还应该增加一下waitGroup的内容,这样主线程在使用的时候会等待所有的协程结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值