需求来源
原本有一个单线程执行器,用来执行多任务计算,但是单线程执行并没有很好利用系统多核 CPU 资源的优势。于是,需要把执行器改成并发执行的执行器。
首先,有一点需要明确的是,需要的进行计算的多任务并不是一次就抛出来的。这是一个实际中遇到的真实的情况。
任务的来源,是一个单线程的计算结果,至于单线程内部做了什么事情,这里不需要关心,重要的是该单线程的返回结果,如果不抛出异常,有可能可以返回多个可并发执行的任务,也可能一个任务也不会返回,但是下一次在执行单线程,有可能能返回可并发执行任务。
这是因为前面一个批次的某个任务或者某几个任务包含后面任务需要的关键输入参数、如果参数得不到满足、单线程不会将任务抛出。只有当任务需要的关键参数全部满足,单线程才会将任务抛出来。
当任务被单线程抛出之后,需要充分调动多核CPU资源的优势,进行并行计算,同时又要作出限制,不能随便就出现内存溢出这种情况。
所以,构造线程池就成为了解决方案之一。
构造方案
// 定义线程池
type HashSyncWorker struct {
workerCount uint32
workers map[uint32]*SyncWorker
}
// 创建 workers 个线程,并给每个线程创建一个 goroutine
// 每个 goroutine 监听一个 channel ,并给channel 设置缓冲区大小为 size
func NewHashSyncWorker(workers uint32, size int) *HashSyncWorker {
hashWorker := &HashSyncWorker {
workerCount: workers,
workers: make(map[uint32]*SyncWorker),
}
var i uint32
for i = 0; i < workers; i ++ {
hashWorker.workers[i] = NewSyncWorker(size, 1)
}
return hashWorker
}
func _hashStrToInt(s string) uint32 {
h := fnv.New32a()
h.Write([]byte(s))
return h.Sum32()
}
// 将任务以 hash 方式投入线程池
func (syncer *HashSyncWorker)IssueOperation(key string, data interface{}, fn func(interface{})error,
sync bool, timeout int) error {
workerIndex := _hashStrToInt(key) % syncer.workerCount
return syncer.workers[workerIndex].IssueOperation(data, fn, sync, timeout)
}
type syncWorkTask struct {
fn func(interface{}) error
data interface{}
sync bool
resp chan bool
err error
}
type SyncWorker struct {
tasks chan *syncWorkTask
lock sync.Mutex
}
func NewSyncWorker(size int, workers int) *SyncWorker {
syncer := &SyncWorker{
tasks: make(chan *syncWorkTask, size),
busy: false,
}
for i := 0; i < workers; i ++ {
go func() {
var task *syncWorkTask = nil
for {
task = <- syncer.tasks
syncer.HandleTask(task)
task = nil
}
}()
}
return syncer
}
func (syncer *SyncWorker)IssueOperation(data interface{}, fn func(interface{})error, sync bool, timeout int) error {
task := &syncWorkTask {
data: data,
fn: fn,
sync: sync,
}
if sync {
task.resp = make(chan bool, 1)
} else {
task.resp = nil
}
syncer.tasks <- task
if sync {
if timeout > 0 {
select {
case <- time.After(time.Duration(timeout) * time.Second):
return errors.New("The sync operation is timeout")
case <- task.resp:
return task.err
}
} else {
<- task.resp
return task.err
}
}
return nil
}
任务投递及结果
采用了线程池,并且任务通过hash 方式向线程池投递,但是,仅仅只是向线程池进行任务投递,而没有进行任务计算结果分析进行回调。只算是完成了高并行运算,持续改进中。
为充分利用多核CPU资源,将原有的单线程执行器改造为并发执行器。任务由单线程产生,根据前批任务的输出决定本次可并发执行的任务数。线程池用于并行计算,通过哈希方式分配任务,但目前未实现任务结果的回调分析,后续将继续优化。
2130

被折叠的 条评论
为什么被折叠?



