2012-01-14 42 views
14

Tôi đang cố gắng chạy tập lệnh Python từ ứng dụng Cocoa. Nó làm việc tốt trên thread chính, nhưng tôi muốn có nó chạy trong nền, trên một hàng đợi GCD đồng thời.Chạy tập lệnh Python từ ứng dụng Cocoa bằng GCD

Tôi đang sử dụng các phương pháp sau đây để thiết lập một lớp học quản lý chạy các script 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); 
} 

Sau đó kịch bản là (nhiều lần) gọi từ phương pháp dụ sau đây, sử dụng một ví dụ singleton chia sẻ của lớp quản lý:

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

các Objective-C móc mã trên vào mã Python sau:

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) 

Điều này làm việc khi tôi luôn chạy các phương pháp Objective-C ở trên từ hàng đợi chính, nhưng kịch bản trả về null khi chạy từ bất kỳ hàng đợi nào khác. Ai đó có thể giải thích cho tôi nếu những gì tôi đang cố gắng làm chỉ là không được hỗ trợ, và liệu có một cách tốt xung quanh nó?

Các tập lệnh Python được gọi thường xuyên và chạy lâu, do đó, thực hiện điều đó trên chuỗi chính sẽ quá chậm, sẽ chạy nó tạo thành hàng đợi nối tiếp. Ngoài ra, tôi muốn chứa mã đồng thời trong Objective-C càng nhiều càng tốt.

Cảm ơn,

+0

Không đủ thông tin trong ví dụ này để thực sự biết bạn đang làm gì. – jkh

+1

Bạn có thể cho tôi biết cách bạn tích hợp nhị phân Python với xcode không? – bijan

Trả lời

11

Từ this page, có vẻ như có một số một số lo ngại luồng khá phức tạp cụ thể để nhúng python. Có lý do nào mà bạn không thể chạy các tập lệnh này trong một quy trình riêng biệt không? Ví dụ, phương thức -runBunchOfScripts sau sẽ chạy kịch bản mười lần (bằng cách gọi -runPythonScript) trên hàng đợi nền song song, thu thập kết quả đầu ra thành một chuỗi các chuỗi và sau đó gọi đối tượng của bạn trở lại chủ đề chính. :

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

Đương nhiên cách tiếp cận của việc sử dụng các quy trình riêng biệt có giới hạn của nó, không phải là ít trong số đó là giới hạn cứng về số lượng của các quá trình, bạn có thể khởi động, nhưng có vẻ như ít hơn rất nhiều đầy nguy hiểm vì nhúng toàn bộ thông dịch viên. Tôi sẽ nói rằng trừ khi bạn cần phải tương tác chattily giữa các kịch bản và môi trường lưu trữ, đây sẽ là một cách tiếp cận tốt hơn.

+0

Cảm ơn, @ipmcc. Rất tiếc, các tập lệnh sẽ phải chạy nhiều lần. Đủ để đạt đến giới hạn khó khăn đó quá sớm. Đó là lý do tại sao tôi đã hy vọng thiết lập môi trường Python một lần, chỉ để lại chi phí tối thiểu cho mỗi khi một tập lệnh được chạy. Trong thực tế, kịch bản lệnh được gửi đến trình thực thi Python như là một NSString, được đánh giá bằng cách sử dụng một phiên bản "an toàn" của 'eval'. –

+3

Bạn có thể làm một giải pháp tương tự với một NSTask; Bắt đầu một (hoặc n) nhiệm vụ phiên dịch Python dài và có đọc kịch bản từ stdin và 'eval' chúng, và sau đó bạn có thể thu thập đầu ra từ tiêu chuẩn ra ngoài. Nếu bạn có thể tương tác với các tập lệnh của mình bằng cách sử dụng các chuỗi, điều này sẽ vẫn hợp lý hơn việc nhúng trình thông dịch (ít nhất là từ nơi tôi đang ngồi). – ipmcc

+0

Rực rỡ. Điều đó chắc chắn nên làm điều đó! –

Các vấn đề liên quan