2012-01-14 9 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); 
} 

れた後、マネージャクラス:

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) 
次のPythonコードに

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

上記Objective-Cのコードフック

私は上記のObjective-Cメソッドを常にメインキューから実行しますが、他のキューから実行すると、このスクリプトはnullを返します。私がしようとしていることがちょうどサポートされていない場合、そしてその周りに良い方法があるかどうか、誰かが私に説明することができますか?

Pythonスクリプトは頻繁に呼び出され、長時間実行されるため、メインスレッドでこれを行うのは遅すぎるため、シリアルキューを実行することになります。さらに、Objective-C内にできるだけ並行性コードを含めることをお勧めします。 Pythonの埋め込みに固有のいくつかのかなり複雑なスレッドの懸念があるよう

おかげで、this pageから

+0

実際に行っていることを実際に知るには、この例の情報が不十分です。 – jkh

+1

xcodeにPythonバイナリをどのように統合したか教えてください。 – bijan

答えて

11

は、それが見えます。これらのスクリプトを別々のプロセスで実行できない理由はありますか?たとえば、次の-runBunchOfScriptsメソッドは、並列バックグラウンドキューで10回(-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'を使って評価されます。 –

+3

NSTaskを使用して同様のソリューションを実行できます。長時間実行しているPythonインタープリタタスクを1つ(またはn)起動し、stdinからスクリプトを読み込み、それらを 'eval'して、標準出力から出力を収集することができます。文字列だけを使ってスクリプトとやりとりすることができるのであれば、少なくとも私が座っている場所からインタプリタを埋め込むよりも理にかなっています。 – ipmcc

+0

Brilliant。それは間違いなくそれを行う必要があります! –

関連する問題