Go 把一片分成N片

Go 把一片分成N片,go,split,slice,Go,Split,Slice,我正在尝试实现一个函数,该函数将TCP端口的一部分拆分为x个其他部分。这些切片将被发送给将扫描这些端口的工作者,因此x由工作者的数量设置 代码如下: //createJobs将端口从指定协议扫描为相等的数目 //将返回的工作的数量。 func(t*Target)createJobs(原型字符串)([]jobMsg,错误){ //初始化作业片 作业:=[]作业消息{} //检查协议的一致性 如果u,确定:=t.portsToScan[proto];!确定{ 返回nil,fmt.Errorf(“当前

我正在尝试实现一个函数,该函数将TCP端口的一部分拆分为x个其他部分。这些切片将被发送给将扫描这些端口的工作者,因此x由工作者的数量设置

代码如下:

//createJobs将端口从指定协议扫描为相等的数目
//将返回的工作的数量。
func(t*Target)createJobs(原型字符串)([]jobMsg,错误){
//初始化作业片
作业:=[]作业消息{}
//检查协议的一致性
如果u,确定:=t.portsToScan[proto];!确定{
返回nil,fmt.Errorf(“当前协议列表中没有这样的协议%q”,proto)
}
//如果proto是ICMP,我们不需要扫描端口
如果proto==“icmp”{
return[]jobMsg{
jobMsg{ip:t.ip,协议:proto},
},零
}
步骤:=(len(t.portsToScan[proto])+t.workers-1)/t.workers
对于i:=0;i
下面是相应的单元测试:

func TestTarget_createJobs(t *testing.T) {
    tests := []struct {
        name         string
        pts          map[string][]string
        workersCount int
        wantErr      bool
    }{
        {
            name:         "5-1",
            pts:          map[string][]string{"tcp": []string{"1", "2", "3", "4", "5"}},
            workersCount: 1,
        },
        {
            name:         "5-2",
            pts:          map[string][]string{"tcp": []string{"1", "2", "3", "4", "5"}},
            workersCount: 2,
        },
        {
            name:         "5-3",
            pts:          map[string][]string{"tcp": []string{"1", "2", "3", "4", "5"}},
            workersCount: 3,
        },
        {
            name:         "5-4",
            pts:          map[string][]string{"tcp": []string{"1", "2", "3", "4", "5"}},
            workersCount: 4,
        },
        {
            name:         "5-5",
            pts:          map[string][]string{"tcp": []string{"1", "2", "3", "4", "5"}},
            workersCount: 5,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            tg := &Target{
                portsToScan: tt.pts,
                workers:     tt.workersCount,
            }
            got, err := tg.createJobs("tcp")
            if (err != nil) != tt.wantErr {
                t.Errorf("Target.createJobs() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if len(got) != tt.workersCount {
                t.Errorf("Target.createJobs() = %d, wanted %d jobs; joblist %v", len(got), tt.workersCount, got)
            }
        })
    }
}


func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}
初始端口列表保存在
t.portsToScan[proto]
中,工作进程数(因此我要创建的片数)由
t.workers
设置


最后,
len(jobs)
必须等于
t.workers
,但我找不到如何做到这一点。

您的算法使用
step
作为批大小:

step := (len(t.portsToScan[proto]) + t.workers - 1) / t.workers
这不是最佳尺寸。例如,如果要扫描4个端口和3个工作进程,这将导致
step=2
,这意味着您只有2个作业(
2+2=4
)。但最好(更优化)有3个批次(尺寸
2+1+1=4

所以批量的大小应该是

defSize := len(t.portsToScan[proto]) / t.workers
问题在于,如果长度不是t.workers的倍数,则最后的一些元素(端口)将不会分配给任何作业。对所有作业使用
defSize+1
将太多

因此,最佳解决方案位于“中间”:一些作业将具有要扫描的
defSize
端口,而一些作业将具有
defSize+1
。必须有多少个
defSize+1
?如果所有人都有
defSize
,则丢失的数量为:

numBigger := len(t.portsToScan[proto]) - defSize*t.workers
请注意,如果要扫描的端口数少于worker,则上述计算会产生
defSize=0
,因此一些worker将获得要扫描的
0
端口,而一些worker将获得
1
。这没关系,但不应该添加带有
0
端口的作业进行扫描

使用此分发:

defSize := len(t.portsToScan[proto]) / t.workers
numBigger := len(t.portsToScan[proto]) - defSize*t.workers

size := defSize+1
for i, idx := 0, 0; i < t.workers; i++ {
    if i == numBigger {
        size--
        if size == 0 {
            break // 0 ports left to scan
        }
    }
    jobs = append(jobs, jobMsg{
        ip:       t.ip,
        protocol: proto,
        ports:    t.portsToScan[proto][idx : idx+size],
    })
    idx += size
}
defSize:=len(t.portsToScan[proto])/t.workers
numBigger:=len(t.portsToScan[proto])-defSize*t.workers
大小:=defSize+1
对于i,idx:=0,0;i
非常感谢您提供清晰完整的答案!遵循你的思维方式真的很有帮助!
defSize := len(t.portsToScan[proto]) / t.workers
numBigger := len(t.portsToScan[proto]) - defSize*t.workers

size := defSize+1
for i, idx := 0, 0; i < t.workers; i++ {
    if i == numBigger {
        size--
        if size == 0 {
            break // 0 ports left to scan
        }
    }
    jobs = append(jobs, jobMsg{
        ip:       t.ip,
        protocol: proto,
        ports:    t.portsToScan[proto][idx : idx+size],
    })
    idx += size
}