I\'m trying to run a Python script from a Cocoa app. It\'s working just fine on the main thread, but I\'d like to have it running in the background, on a concurrent GCD queue.>
From this page, it looks like there are some some pretty complex threading concerns specific to embedding python. Is there a reason you couldn't just run these scripts in a separate process? For instance, the following -runBunchOfScripts
method would run the script ten times (by calling -runPythonScript
) on a parallel background queue, collecting the resulting outputs into an array of strings, and then calling your object back on the main thread once all the scripts has completed:
- (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...");
}
Naturally the approach of using separate processes has it's limitations, not the least of which being the hard limit on the number of processes you can launch, but it seems a lot less fraught with peril than embedding the entire interpreter. I would say that unless you need to interact chattily between the scripts and the hosting environment, this would be a better approach.