使用GCD从Cocoa应用程序运行Python脚本 [英] Running Python script from Cocoa application using GCD

查看:169
本文介绍了使用GCD从Cocoa应用程序运行Python脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从Cocoa应用程序运行Python脚本.它在主线程上工作得很好,但是我希望它在并发GCD队列中在后台运行.

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.

我正在使用以下方法来设置运行Python脚本的管理器类:

I'm using the following method to setup a manager class that runs the Python script:

- (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);
}

之后,使用管理器类的共享单例实例从以下实例方法(反复)中调用脚本:

After which the script is (repeatedly) called from the following instance method, using a shared singleton instance of the manager class:

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

上面的Objective-C代码与以下Python代码挂钩:

The above Objective-C code hooks into the following Python code:

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.有人可以向我解释一下我所尝试做的事情是否只是不受支持,以及是否有很好的解决方法?

This works when I always run the above Objective-C methods from the main queue, but the script returns null when run from any other queue. Could someone explain me if what I'm trying to do is just not supported, and whether there's a good way around it?

Python脚本经常被调用并且运行很长时间,因此在主线程上这样做太慢了,将其运行在一个串行队列中.另外,我想将并发代码尽可能地包含在Objective-C中.

The Python scripts is called often and runs long, so doing that on the main thread would be too slow, a would be running it form a serial queue. In addition, I'd like to contain the concurrency code within Objective-C as much as possible.

谢谢

推荐答案

此页面开始,它似乎有些特定于嵌入python的非常复杂的线程问题.您是否有理由不能仅在单独的过程中运行这些脚本?例如,以下-runBunchOfScripts方法将在并行后台队列上运行脚本十次(通过调用-runPythonScript),将结果输出收集到字符串数组中,然后一次在主线程上再次调用您的对象脚本已完成:

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.

这篇关于使用GCD从Cocoa应用程序运行Python脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆