2012-01-14 50 views
14

我想从Cocoa应用程序运行Python脚本。它在主线程上工作得很好,但我想让它在后台运行,并发GCD队列。使用GCD从Cocoa应用程序运行Python脚本

我用下面的方法来建立一个运行Python脚本经理类:

- (BOOL)setupPythonEnvironment { 
    if (Py_IsInitialized()) return YES; 

    Py_SetProgramName("/usr/bin/python"); 
    Py_Initialize(); 

    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"MyScript"  ofType:@"py"]; 

    FILE *mainFile = fopen([scriptPath UTF8String], "r"); 
    return (PyRun_SimpleFile(mainFile, (char *)[[scriptPath lastPathComponent] UTF8String]) == 0); 
} 

在这之后的脚本(反复)从下面的实例方法调用,使用一个共享的单一实例该管理器类:

- (id)runScriptWithArguments:(NSArray *)arguments { 
    return [NSClassFromString(@"MyScriptExecutor") runWithArguments:arguments]; 
} 

上面Objective-C代码挂钩插入下面的Python代码:

from Foundation import * 

def run_with_arguments(arguments): 
# ...a long-running script 

class MyScriptExecutor(NSObject): 
    @classmethod 
    def runWithArguments_(self, arguments): 
     return run_with_arguments(arguments) 

当我总是从主队列中运行上述Objective-C方法时,这种方法有效,但是当从任何其他队列运行时,脚本将返回null。有人可以解释我,如果我试图做的只是不被支持,是否有一个好的方法呢?

Python脚本经常被调用并且运行时间很长,所以在主线程上执行该脚本会太慢,一个会运行它形成一个串行队列。另外,我想尽可能在​​Objective-C中包含并发代码。

感谢,

+0

没有足够的信息在这个例子中真正知道你在做什么。 – jkh 2012-01-15 06:58:38

+1

你能告诉我你是如何将Python二进制文件与xcode集成的吗? – bijan 2012-10-22 12:10:23

回答

11

this page,它看起来像有一些专门针对嵌入蟒蛇一些非常复杂的线程的担忧。是否有理由不能在单独的过程中运行这些脚本?例如,以下-runBunchOfScripts方法会在并行背景队列上运行脚本十次(通过调用-runPythonScript),将结果输出收集到一个字符串数组中,然后在所有脚本完成后再将对象调回主线程:

- (NSString*)runPythonScript 
{ 
    NSTask* task = [[[NSTask alloc] init] autorelease]; 
    task.launchPath = @"/usr/bin/python"; 
    NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"MyScript" ofType:@"py"]; 
    task.arguments = [NSArray arrayWithObjects: scriptPath, nil]; 

    // NSLog breaks if we don't do this... 
    [task setStandardInput: [NSPipe pipe]]; 

    NSPipe *stdOutPipe = nil; 
    stdOutPipe = [NSPipe pipe]; 
    [task setStandardOutput:stdOutPipe]; 

    NSPipe* stdErrPipe = nil; 
    stdErrPipe = [NSPipe pipe]; 
    [task setStandardError: stdErrPipe]; 

    [task launch];   

    NSData* data = [[stdOutPipe fileHandleForReading] readDataToEndOfFile]; 

    [task waitUntilExit]; 

    NSInteger exitCode = task.terminationStatus; 

    if (exitCode != 0) 
    { 
     NSLog(@"Error!"); 
     return nil; 
    } 

    return [[[NSString alloc] initWithBytes: data.bytes length:data.length encoding: NSUTF8StringEncoding] autorelease]; 
} 

- (void)runBunchOfScripts 
{ 
    dispatch_group_t group = dispatch_group_create(); 
    NSMutableArray* results = [[NSMutableArray alloc] init]; 
    for (NSUInteger i = 0; i < 10; i++) 
    { 
     dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
      NSString* result = [self runPythonScript]; 
      @synchronized(results) 
      { 
       [results addObject: result]; 
      } 
     }); 
    } 

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
     [self scriptsDidFinishWithResults: results]; 
     dispatch_release(group); 
     [results release]; 
    }); 
} 

- (void)scriptsDidFinishWithResults: (NSArray*)results 
{ 
    NSLog(@"Do something with the results..."); 
} 

当然使用独立的进程的方法有它的局限性,而不是其中最重要的是对的,你可以启动的进程数的硬性限制,但它似乎有危险不是嵌入整个少了很多充满翻译。我会说,除非你需要在脚本和主机环境之间进行聊天交互,否则这将是一个更好的方法。

+0

谢谢,@ipmcc。不幸的是,脚本将不得不运行多次。足以很快达到这个硬性限制。这就是为什么我曾希望设置一次Python环境,每次脚本运行时只留下最小的开销。事实上,该脚本作为NSString发送给Python执行程序,使用“安全”版本的“eval”进行评估。 – 2012-01-16 12:42:33

+3

你可以用NSTask做类似的解决方案;启动一个(或n个)长时间运行的Python解释器任务,并从stdin和'eval'中读取脚本,然后可以从标准输出收集输出。如果你只能使用字符串与你的脚本进行交互,这将比嵌入解释器更有意义(至少从我坐的位置开始)。 – ipmcc 2012-01-16 16:26:34

+0

辉煌。这绝对应该做到! – 2012-01-16 18:40:31

相关问题