Obj-C设计模式:并行任务启动器



我目前有一个shell脚本,在GraphicsMagick的帮助下,一个接一个地处理许多图像。它运行良好,所有的计算都是正确的,一切正常。(这不是一个"简单"的脚本,它涉及到从JSON文件读取尺寸,根据许多约束转换一堆图像)。

当我们使用双核或四核计算机时,我想并行化它。作为一个iPhone开发者,我喜欢向Mac开发介绍自己,我想用XCode和Objective-C使用"命令行工具"模板创建它。

到目前为止一切顺利,但是现在我要面对"任务分派器"对象的设计。在运行循环中运行NSTasks,在单独的线程中,使用块,带或不带GCD,带或不带ARC之间,我相当迷失。

如何做到这一点?我正在考虑使用简单的线程来生成NSTasks,让它们在完成时报告,并通知我的调度程序的委托,以便它可以升级其进度条。但我真的很想联系中央调度中心。有没有人有什么想法,想法,建议,什么该做什么不该做?

编辑:我正在阅读苹果的文档,并找到了NSOperationQueue类。有没有可能这个正是我在这里需要的 ?

用于启动包括参数和环境变量在内的独立进程的一个好类是NSTask。有关详细信息,请参阅文档。下面是一个小命令行工具,它启动10个并发进程并等待它们完成。NSOperationQueue在这里是多余的,因为任务已经并发地启动了。

——编辑:改进版本限制并发——

int main (int argc, const char * argv[])
{
   @autoreleasepool {
       // Let's not have more than 5 parallel processes
       dispatch_semaphore_t limit = dispatch_semaphore_create(5);
       dispatch_semaphore_t done  = dispatch_semaphore_create(0);
       for (int i=0;  i<10;  i++) {
           // Setup the taks as you see fit including the environment variables.
           // See docs on NSTask for more on how to use this object.
           NSTask *task = [[NSTask alloc] init];
           task.launchPath = @"/bin/ls";
           task.arguments = [NSArray arrayWithObject:@"-la"];
           task.terminationHandler = ^(NSTask *task) {
               dispatch_semaphore_signal(limit);
               if (i==9) dispatch_semaphore_signal(done);
           };
           dispatch_semaphore_wait(limit, DISPATCH_TIME_FOREVER);
           [task launch];
       }
       dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
       dispatch_release(limit);
       dispatch_release(done);
   }
   return 0;

}

—原版本—

int main (int argc, const char * argv[])
{
    @autoreleasepool {
        NSObject *lock = [[NSObject alloc] init];
        int __block counter = 10;
        for (int i=0;  i<10;  i++) {
            // Setup the taks as you see fit including the environment variables.
            // See docs on NSTask for more on how to use this object.
            NSTask *task = [[NSTask alloc] init];
            task.launchPath = @"/bin/ls";
            task.arguments = [NSArray arrayWithObject:@"-la"];
            task.terminationHandler = ^(NSTask *task) {
                @synchronized(lock) { counter--; }
            };
            [task launch];
        }
        while (counter)
            usleep(50);
        [lock release];
    }
    return 0;
}

在您的情况下,您可能希望将NSTask对象保存在一个数组中,以便于管理。

yes - NSOperation/NSOperationQueue适合此任务

我会这样开头:

@protocol MONTaskRequestDelegate
- (void)taskRequestDidComplete:(MONTaskRequest *)taskRequest;
@end
@interface MONTaskRequest : NSOperation
{
@private
    NSTask * task;
    NSObject<MONTaskRequestDelegate>* delegate; /* strong reference. cleared on cancellation and completion, */
}
- (id)initWithTask:(NSTask *)task delegate:(NSObject<MONTaskRequestDelegate>*)delegate;
// interface to access the data from the task you are interested in, whether the task completed, etc.
@end
@implementation MONTaskRequest
// ...
- (void)performDelegateCallback
{
    [self.delegate taskRequestDidComplete:self];
    self.delegate = nil;
}
- (void)main
{
    NSAutoreleasePool * pool = [NSAutoreleasePool new];
    [self runTheTask];
    // grab what is needed and handle errors
    [self performDelegateCallback];
    [pool release];
}
- (void)cancel
{
    [super cancel];
    [self stopTaskIfPossible];
    [self performDelegateCallback];
}
@end

那么你可以使用NSOperationQueue来限制活动任务的数量到一个合理的数量

我为此目的使用[myObj performSelectorInBackground:@selector(doSomething) withthobject:nil];NSObject的功能

这个想法很简单:你写一个方法来完成这项工作,使用前面提到的方法从主线程调用它,然后调用一些回调选择器,如果你需要以某种方式处理来自不同线程的结果。

最新更新