我有一个归结为:
的功能while(doWork)
{
config = generateConfigurationForTesting();
result = executeWork(config);
doWork = isDone(result);
}
如果所有函数都是线程安全的,独立于以前的迭代,并且可能需要比最大允许线程数更多的迭代,我怎样才能重写这个以实现高效的异步执行?
这里的问题是我们不知道需要提前多少次迭代才能生成dispatch_group
或使用dispatch_apply
。
这是我的第一次尝试,但由于任意选择的值和睡眠,它看起来有点难看;
int thread_count = 0;
bool doWork = true;
int max_threads = 20; // arbitrarily chosen number
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
while(doWork)
{
if(thread_count < max_threads)
{
dispatch_async(queue, ^{ Config myconfig = generateConfigurationForTesting();
Result myresult = executeWork();
dispatch_async(queue, checkResult(myresult)); });
thread_count++;
}
else
usleep(100); // don't consume too much CPU
}
void checkResult(Result value)
{
if(value == good) doWork = false;
thread_count--;
}
答案 0 :(得分:1)
根据您的描述,看起来generateConfigurationForTesting
是某种随机化技术或者是一种可以进行近乎无限数量配置的生成器(因此您的评论提前您不知道如何你需要多次迭代)。以此为假设,您基本上坚持使用您创建的模型,因为您的执行程序需要受到关于队列的一些合理假设的限制,并且您不希望过度生成,因为这只会扩展成功找到value ==good
测量值后的运行长度。
我建议您考虑使用队列(或OSAtomicIncrement*
和OSAtomicDecrement*
)来保护对thread_count
和doWork
的访问权限。就目前而言,thread_count
递增和递减将发生在两个不同的队列中(主线程的main_queue和后台任务的默认队列),因此可以同时递增和递减线程计数。这可能会导致计数不足(这会导致创建的线程数超出预期)或计入超量计数(这会导致您永远无法完成任务)。
使这个看起来更好一点的另一个选择是checkResult
如果value!=good
将新元素添加到队列中。这样,您可以使用dispatch_apply( 20, queue, ^{ ... })
加载队列的初始元素,而根本不需要thread_count
。前20个将使用dispatch_apply
(或dispatch_apply
认为适合您的配置的金额)添加,然后每次调用checkResult
时,您可以设置doWork=false
或向queue
添加另一项操作。
答案 1 :(得分:1)
dispatch_apply()
适用于此,只需传递ncpu作为迭代次数(apply永远不会使用超过ncpu工作线程)并保持工作块的每个实例运行,只要还有更多工作要做(即循环回generateConfigurationForTesting()
,除非!doWork
)。