我正在尝试从Cocoa应用程序运行Python脚本。它在主线程上运行得很好,但是我希望它在后台运行,并发GCD队列。
我正在使用以下方法来设置运行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);
}
之后,使用manager类的共享单例实例从以下实例方法中重复调用脚本:
- (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 :(得分:13)
从this page开始,看起来有一些非常复杂的线程问题,特别是嵌入python。您是否有理由不能在单独的进程中运行这些脚本?例如,以下-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...");
}
自然地,使用单独进程的方法有其局限性,其中最重要的是对可以启动的进程数量的硬性限制,但它似乎比嵌入整个解释器要少得多。我会说,除非你需要在脚本和托管环境之间闲聊,否则这将是一种更好的方法。