2012-01-14 108 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

相關問題