NSTask с семафором в многопоточном режиме застрял в ожиданииUntilExit

Мое приложение Cocoa имеет несколько потоков (до 8 потоков), запускающих скрипт Python с использованием NSTask, что занимает около 1200 секунд. Почти при каждом выполнении 8 потоков, выполняющих NSTask, приложение просто ждет почти вечность и никогда не возвращается. Я приостановил выполнение и заметил, что приложение застряло в ожидании до выхода. Мой фрагмент кода, как показано ниже. Пожалуйста, помогите, дайте мне знать, как я могу решить эту проблему.

  dispatch_semaphore_t MySemaphore;

...

MySemaphore = dispatch_semaphore_create(8);

...

//Code portion where NSTask is used to execute a python script.
...
NSTask* task = [NSTask new];

    [task setLaunchPath:launchPath];
    [task setArguments:[NSArray arrayWithObjects:param1,
                                param2, param3,
                                param4, param5,
                                param6, param7, nil]];
    NSData *outData;
    NSData *errorData;
    int retVal;


         dispatch_semaphore_wait(MySemaphore, DISPATCH_TIME_FOREVER);

        retVal = [self _runTask:task
                             inData:nil
                            outData:&outData
                          errorData:&errorData];
        dispatch_semaphore_signal(MySemaphore);

    NSMutableString *retStr = [[[NSMutableString alloc] initWithData:outData
                                                                 encoding:NSUTF8StringEncoding] autorelease];
    NSMutableString *retStrErr = [[[NSMutableString alloc] initWithData:errorData
                                                                       encoding:NSUTF8StringEncoding] autorelease];

        if([retStrErr length] != 0)
        {
            [retStr appendString:@"\nstandard Error output:\n"];
            [retStr appendString:retStrErr];
        }

        if( retVal == 0 )
        {
            [retStr appendString:@"\nOK\n"];

        }
        else
        {
            [retStr appendString:@"\nERROR\n"];
        }

        NSLog(@"Response of _DRAMRetention for UUT:%d, %@", nUut+1, retStr);

        [task release];

...

- (int) _runTask:(NSTask*)task inData:(NSData*)inData outData:(NSData**)outData errorData:(NSData**)errorData
{
    NSPipe*             inPipe = nil;
    NSPipe*             outPipe = nil;
    NSPipe*             errorPipe = nil;
    NSFileHandle*       fileHandle;

    /* Reset output variables */
    if(outData)
    {
        *outData = nil;
    }

    if(errorData)
    {
        *errorData = nil;
    }
    /* Safe check */
    if(task == nil)
    {
        return -1;
    }
    /* Create standard input pipe */
    if([inData length])
    {  
        if((inPipe = [NSPipe new]))
        {
            [task setStandardInput:inPipe];
            [inPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Create standard output pipe */
    if(outData)
    {
        if((outPipe = [NSPipe new]))
        {
            [task setStandardOutput:outPipe];
            [outPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Create standard error pipe */
    if(errorData)
    {
        if((errorPipe = [NSPipe new]))
        {
            [task setStandardError:errorPipe];
            [errorPipe release];
        }
        else
        {
            task = nil;
            goto Exit;
        }
    }

    /* Launch task */
    NS_DURING
    [task launch];
    NS_HANDLER
    task = nil;
    NS_ENDHANDLER
    if(task == nil)
    {
        goto Exit;
    }
    /* Write data to standard input pipe */
    if((fileHandle = [inPipe fileHandleForWriting]))
    {
        NS_DURING
        [fileHandle writeData:inData];
        [fileHandle closeFile];
        NS_HANDLER
        [task terminate];
        [task interrupt];
        task = nil;
        NS_ENDHANDLER
    }
    if(task == nil)
    {
        goto Exit;
    }
    /* Wait for task to complete and read data from standard output and standard error pipes in background */
    if((fileHandle = [outPipe fileHandleForReading]))
    {
        *outData = [NSMutableData data];
        [[NSNotificationCenter defaultCenter] addObserver:*outData selector:@selector(_CommandLineToolFileHandleDataAvailable:) name:NSFileHandleDataAvailableNotification object:fileHandle];
        [fileHandle waitForDataInBackgroundAndNotify];
    }
    if((fileHandle = [errorPipe fileHandleForReading]))
    {
        *errorData = [NSMutableData data];
        [[NSNotificationCenter defaultCenter] addObserver:*errorData selector:@selector(_CommandLineToolFileHandleDataAvailable:) name:NSFileHandleDataAvailableNotification object:fileHandle];
        [fileHandle waitForDataInBackgroundAndNotify];
    }

    //My app is stuck here
    [task waitUntilExit];

    if((fileHandle = [errorPipe fileHandleForReading]))
    {
        [[NSNotificationCenter defaultCenter] removeObserver:*errorData name:NSFileHandleDataAvailableNotification object:fileHandle];
        [(NSMutableData*)*errorData appendData:[fileHandle readDataToEndOfFile]];
    }
    if((fileHandle = [outPipe fileHandleForReading]))
    {
        [[NSNotificationCenter defaultCenter] removeObserver:*outData name:NSFileHandleDataAvailableNotification object:fileHandle];
        [(NSMutableData*)*outData appendData:[fileHandle readDataToEndOfFile]];
    }

Exit:
    [[inPipe fileHandleForReading] closeFile];
    [[inPipe fileHandleForWriting] closeFile];
    [[outPipe fileHandleForReading] closeFile];
    [[outPipe fileHandleForWriting] closeFile];
    [[errorPipe fileHandleForReading] closeFile];
    [[errorPipe fileHandleForWriting] closeFile];

    return (task ? [task terminationStatus] : -1);
}

@end

@implementation NSMutableData (CommandLineTool)

/* Extend the NSMutableData class to add a method called by   NSFileHandleDataAvailableNotification to automatically append the new data */
- (void) _CommandLineToolFileHandleDataAvailable:(NSNotification*)notification
{
    NSFileHandle*           fileHandle = [notification object];

    [self appendData:[fileHandle availableData]];

    [fileHandle waitForDataInBackgroundAndNotify];
}

person albertleng    schedule 29.05.2015    source источник


Ответы (1)


Во-первых, вам не нужно повторно вызывать [fileHandle waitForDataInBackgroundAndNotify];. Возможно, причина не в вашей проблеме...

Во-вторых, в подобных случаях это почти всегда происходит из-за проблем с буферизацией. Но вы обратились к этому. Вторая наиболее распространенная причина заключается в том, что задача не завершается.

Вы уверены, что скрипты Python на самом деле завершаются?

person bbum    schedule 29.05.2015
comment
Да, я уверен, что скрипты Python завершаются. Что еще вы предлагаете мне сделать, чтобы решить эту проблему с ожиданием навсегда в nstask waituntilexit? - person albertleng; 31.05.2015
comment
@user2370312 user2370312 Помимо проверки того, что все данные считываются из выходных каналов, я не уверен. Я пытаюсь придумать, что еще могло бы заблокировать завершение и появление пустого места. - person bbum; 31.05.2015