需求來源
原本有一個單線程執行器,用來執行多任務計算,但是單線程執行並沒有很好利用系統多核 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 方式向線程池投遞,但是,僅僅只是向線程池進行任務投遞,而沒有進行任務計算結果分析進行回調。只算是完成了高並行運算,持續改進中。