Go 代码干货合集

简单线程池

和 Python 干货合集里面差不多,一个十分简短的线程池应用代码

主要负责:根据函数参数列表,无序批量地完成一系列任务,阻塞等待完成,返回结果列表,提供同时运行的最大线程数控制

package task

import (
	"errors"
	"fmt"
	"math"
	"sync"
)

type AnyCaller func(...any) any

// 同步列表
type SyncList struct {
	items []any      // 列表项
	mutex sync.Mutex // 互斥锁
}

// 每个独立的任务报告类型
type Report struct {
	result any
	err    any
}

func (r *Report) String() string {
	return fmt.Sprintf("result: %v, err: %v", r.result, r.err)
}

func (l *SyncList) Add(items ...any) {
	l.mutex.Lock()
	l.items = append(l.items, items...)
	defer l.mutex.Unlock()
}

func copyList(l []any) []any {
	listCopy := make([]any, len(l))
	copy(listCopy, l)
	return listCopy
}

// 批量随机地完成所有的工作
func execute(f AnyCaller, args [][]any) []any {
	var wg sync.WaitGroup
	var resultList *SyncList = &SyncList{items: make([]any, 0)}

	var l int = len(args)
	for i := 0; i < l; i++ {
		wg.Add(1)
		go func(idx int) {
			defer wg.Done()
			defer func() {
				// 处理未知异常
				if r := recover(); r != nil {
					resultList.Add(&Report{nil, r})
				}
			}()
			returnResult := f(args[idx]...)
			resultList.Add(&Report{returnResult, nil})
		}(i)
	}

	wg.Wait()

	return copyList(resultList.items)
}

// 使用线程池无序地完成一批工作, 返回结果列表
func MultiTask(f AnyCaller, args [][]any, maxTokents int) []any {
	if maxTokents <= 0 {
		panic(errors.New("不能创建空线程池"))
	}
	least := len(args)

	var resultList *SyncList = &SyncList{items: make([]any, least)}

	var taksNum int = int(math.Ceil(float64(least) / float64(maxTokents)))
	for k := 1; k <= taksNum; k++ {
		var right int = k * maxTokents
		right = min(least, right)
		res := execute(f, args[((k-1)*maxTokents):right])

		resultList.Add(res...)
	}
	return copyList(resultList.items)
}

配套的测试代码 multi_test.go

package task

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

func show(a ...int) {
	fmt.Print(a)
}

func slice2str(s []any) string {
	a := fmt.Sprintf("%v", s)
	return a
}

func errorFunc(args []any) {
	// panic("错误函数:" + slice2str(args))
	a := []int{1, 2}
	fmt.Println(a[3])
}

func normalFunc(args []any) string {
	return fmt.Sprintf("正常执行的函数: %v", args)
}

func RandFunc(args ...any) any {
	sec := time.Duration(5)

	time.Sleep(sec * time.Second)
	if rand.Intn(10) >= 7 {
		errorFunc(args)
		return -1
	}
	return normalFunc(args)
}

func TestMultiTask(t *testing.T) {
	rand.New(rand.NewSource(312))

	args := [][]any{
		[]any{1, 2, 3},
		[]any{4, 5, 6},
		[]any{7, 8, 9},
		[]any{10, 11, 12},
		[]any{13, 14, 15},
		[]any{16, 17, 18},
		[]any{19, 20, 21},
		[]any{22, 23, 24},
		[]any{25, 26, 27},
	}

	for _, v := range MultiTask(RandFunc, args, 6) {
		if v != nil {
			t.Log(v)
		}
	}
}

小笔记

创建固定容量的 slice, 千万别把容量写到第二个参数,这样 slice 会预先填充多个 nil 值在前面的位置

make([]any, 0, x)

// 错误写法
make([]any, 3)  // 结果 {nil, nil, nil}

copy 反而认的是 len 的值,就是说创建的 slice 要有预先的 len 值, copy 才能把元素放进去

复制一份 slice

func copyList(l []any) []any {
	listCopy := make([]any, len(l))
	copy(listCopy, l)
	return listCopy
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值