假设您希望5个线程同时处理数据。还假设您有89个要处理的任务。
击球你知道89/5 = 17,剩下的4分。任务拆分的最佳方法是让4个(余数)线程分别处理18(17 + 1)个任务,然后得1( #threads - remainder)线程处理17.
这将消除余数。只是为了验证:
Thread 1: Tasks 1-18 (18 tasks)
Thread 2: Tasks 19-36 (18 tasks)
Thread 3: Tasks 37-54 (18 tasks)
Thread 4: Tasks 55-72 (18 tasks)
Thread 5: Tasks 73-89 (17 tasks)
完成了89项任务。
我需要一种以数学方式获得每个线程的开始和结束范围/可编程性的方法;以下内容应该打印出我上面列出的确切内容:
$NumTasks = 89
$NumThreads = 5
$Remainder = $NumTasks % $NumThreads
$DefaultNumTasksAssigned = floor($NumTasks / $NumThreads)
For $i = 1 To $NumThreads
if $i <= $Remainder Then
$NumTasksAssigned = $DefaultNumTasksAssigned + 1
else
$NumTasksAssigned = $DefaultNumTasksAssigned
endif
$Start = ??????????
$End = ??????????
print Thread $i: Tasks $Start-$End ($NumTasksAssigned tasks)
Next
这也适用于任意数量的$NumTasks
。
注意:请坚持回答手头的数学,避免暗示或假设情况。
答案 0 :(得分:7)
为什么呢?而不是预先确定调度顺序,将所有任务粘贴在队列上,然后让每个线程在准备好时逐个拉出它们。然后你的任务基本上会“尽可能快地”运行。
如果您预先分配,那么一个线程可能正在进行特别长时间的处理并阻止其后面的所有任务的运行。使用队列,当每个任务完成并且线程释放时,它会抓取下一个任务并继续前进。
把它想象成一个银行,每个柜员一行,一行和很多出纳员。在前者中,你可能会被困在存放硬币的人身后并逐一计算出来,后者你会到达下一个可用的出纳员,而PocketChange先生则计算在内。
答案 1 :(得分:1)
我是哈顿的第二句话。您可以一次向它们提供一个任务(或者一次一个地执行一些任务,具体取决于是否有很多开销,即相对于启动/回收线程的成本,单个任务通常非常快地完成)。 em>您的后续评论有效地解释了您的“线程”带来了巨大的创作成本,因此您希望尽可能多地提供一次工作,而不是浪费时间创建新的“线程”,每个人都提供少量工作。
无论如何......转到math question
...
如果您只想分配一次任务,可以使用以下公式代替??????????在你的逻辑中,应该做的诀窍:
$Start = 1
+ (($i -1) * ($DefaultNumTasksAssigned + 1)
- (floor($i / ($Remainder + 1)) * ($i - $Remainder))
$End = $Start + $NumTasksAssigned -1
该公式解释如下:
1表示您的显示/逻辑是基于一的而不是基于零的
第二个术语是因为我们通常在每次迭代时添加($ DefaultNumTasksAssigned + 1)
第三项提供了对最后几次迭代的修正
它的第一部分(floor($i / ($Remainder + 1))
提供0直到$ i到达第一个线程
没有收到一个额外的任务,其后1
第二部分表达了我们需要纠正的程度
$ End的公式更容易,唯一的技巧是减1,这是因为Start和End值是包含的(例如在1和19之间有19个任务不是18)
以下稍微修改过的逻辑片段也应该有用,它通过保持$ Start变量的运行选项卡避免了“花哨”的公式,而不是每次都重新计算它。
$NumTasks = 89
$NumThreads = 5
$Remainder = $NumTasks % $NumThreads
$DefaultNumTasksAssigned = floor($NumTasks / $NumThreads)
$Start = 1
For $i = 1 To $NumThreads
if $i <= $Remainder Then // fixed here! need <= because $i is one-based
$NumTasksAssigned = $DefaultNumTasksAssigned + 1
else
$NumTasksAssigned = $DefaultNumTasksAssigned
endif
$End = $Start + $NumTasksAssigned -1
print Thread $i: Tasks $Start-$End ($NumTasksAssigned tasks)
$Start = $Start + $NumTasksAssigned
Next
以下是上述
的Python转录>>> def ShowWorkAllocation(NumTasks, NumThreads):
... Remainder = NumTasks % NumThreads
... DefaultNumTasksAssigned = math.floor(NumTasks / NumThreads)
... Start = 1
... for i in range(1, NumThreads + 1):
... if i <= Remainder:
... NumTasksAssigned = DefaultNumTasksAssigned + 1
... else:
... NumTasksAssigned = DefaultNumTasksAssigned
... End = Start + NumTasksAssigned - 1
... print("Thread ", i, ": Tasks ", Start, "-", End, "(", NumTasksAssigned,")")
... Start = Start + NumTasksAssigned
...
>>>
>>> ShowWorkAllocation(89, 5)
Thread 1 : Tasks 1 - 18 ( 18 )
Thread 2 : Tasks 19 - 36 ( 18 )
Thread 3 : Tasks 37 - 54 ( 18 )
Thread 4 : Tasks 55 - 72 ( 18 )
Thread 5 : Tasks 73 - 89 ( 17 )
>>> ShowWorkAllocation(11, 5)
Thread 1 : Tasks 1 - 3 ( 3 )
Thread 2 : Tasks 4 - 5 ( 2 )
Thread 3 : Tasks 6 - 7 ( 2 )
Thread 4 : Tasks 8 - 9 ( 2 )
Thread 5 : Tasks 10 - 11 ( 2 )
>>>
>>> ShowWorkAllocation(89, 11)
Thread 1 : Tasks 1 - 9 ( 9 )
Thread 2 : Tasks 10 - 17 ( 8 )
Thread 3 : Tasks 18 - 25 ( 8 )
Thread 4 : Tasks 26 - 33 ( 8 )
Thread 5 : Tasks 34 - 41 ( 8 )
Thread 6 : Tasks 42 - 49 ( 8 )
Thread 7 : Tasks 50 - 57 ( 8 )
Thread 8 : Tasks 58 - 65 ( 8 )
Thread 9 : Tasks 66 - 73 ( 8 )
Thread 10 : Tasks 74 - 81 ( 8 )
Thread 11 : Tasks 82 - 89 ( 8 )
>>>
答案 2 :(得分:0)
我认为你solved the wrong half of your problem。
除非满足以下所有条件,否则几乎不可能准确地确定完成所有任务所需的时间:
实际上,大多数情况下,您的任务是I / O绑定而不是CPU绑定:也就是说,您正在等待某些外部资源,例如从文件读取,从数据库获取或与之通信一台远程电脑。在这种情况下,你只会通过添加更多线程来使事情变得更糟,因为他们都在争夺相同的稀缺资源。
最后,除非你有一些非常奇怪的硬件,否则你实际上不可能同时运行五个线程。 (通常处理器配置至少为两个的倍数。)如果你的任务非常受CPU限制,那么每个CPU的最佳位置大约为1个线程,如果任务花费一半的时间来处理CPU,那么每个CPU大约需要2个线程。他们有一半时间做IO等等。
tl; dr :在我们就此问题向您提出建议之前,我们需要了解您的任务和硬件的更多信息。