2

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

当我们使用双核或四核计算机时,我想并行化它。由于我是一名 iPhone 开发人员,喜欢向自己介绍 Mac 开发,我想使用“命令行工具”模板使用 XCode 和 Objective-C 创建它。

到目前为止一切顺利,但现在我面临着“任务调度程序”对象的设计。在运行循环中,在单独的线程中,使用块,使用或不使用 GCD,使用或不使用 ARC 之间运行 NSTasks 之间,我相当迷失。

一个人将如何实现这一目标?我正在考虑使用简单的线程来生成 NSTask,让它们在完成时报告,并通知我的调度员的委托,以便它可以升级其进度条。但我真的很想与 Grand Central Dispatch 取得联系。有没有人对做什么和不做什么有任何想法、想法和建议?

编辑:我正在阅读 Apple 的文档,并找到了 NSOperationQueue 类。难道这正是我在这里需要的吗?

4

3 回答 3

4

用于启动包括参数和环境变量在内的独立进程的一个很好的类是 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 对象保存在数组中以便于管理。

于 2011-09-06T18:31:38.893 回答
1

是的 - 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将活动任务的数量限制为合理的数量。

于 2011-09-06T19:15:23.170 回答
-1

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

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

于 2011-09-06T11:36:45.790 回答