目标C等待处理异步任务

时间:2014-02-06 06:11:17

标签: objective-c multithreading

在我的代码中,我正在运行本地服务器(CocoaHTTPServer)。当服务器收到请求时,它会创建一个线程并将控制传递给某个方法( - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path,这里可能不相关)。

我需要阅读本地资产列表并返回结果。 API调用([assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) ...)是异步的。

由于HTTPResponse需要等到API调用完成,我创建了一个名为_isProcessing的标志,我在进行API调用之前设置了该标志。调用完成后,我取消设置标志并返回HTTP请求。等待的代码如下:

// the API call is non-blocking. hence, wait in a loop until the command has finished
samCommand->isProcessing = YES;
while (samCommand->isProcessing) {
    usleep(100*1000);
}

API调用在完成任务后调用委托方法,如下所示:

// to be called at the end of an asynch operation (eg: reading local asset list)
- (void) commandDidFinish {
    // flag to open the lock
    isProcessing = NO;
}

这可行,但可能需要性能增强。我如何在这里使用任何东西(运行循环等)来改善性能。


使用dispatch_semaphore

编辑weichsel解决方案

按照weichsel的解决方案,我创建了一个信号量。我的代码序列是:

  1. CocoaHTTPServer接收请求,因此创建新线程
  2. 它调用Command类的静态方法来执行请求
  3. Command类创建一个新命令Object调用另一个类(使用反射)调用ALAsset API并将命令Object传递给它
  4. 返回时,ALAsset API调用将调用委托方法 命令类
  5. 因此我在适当的位置嵌入了信号量。但是,信号量的等待循环有时并没有结束。正常输出应为:

    2014-02-07 11:27:23:214 MM2Beta[7306:1103] HTTPServer: Started HTTP server on port 1978
    2014-02-07 11:27:23:887 MM2Beta[7306:6303] created semaphore 0x1f890670->0x1f8950a0
    2014-02-07 11:27:23:887 MM2Beta[7306:6303] calling execute with 0x1f890670
    2014-02-07 11:27:23:887 MM2Beta[7306:6303] starting wait loop 0x1f890670->0x1f8950a0
    2014-02-07 11:27:23:887 MM2Beta[7306:907] calling getAssetsList with delegate 0x1f890670
    2014-02-07 11:27:24:108 MM2Beta[7306:907] calling delegate [0x1f890670 commandDidFinish]
    2014-02-07 11:27:24:108 MM2Beta[7306:907] releasing semaphore 0x1f890670->0x1f8950a0
    2014-02-07 11:27:24:109 MM2Beta[7306:6303] ending wait loop 0x1f890670->0x0
    

    在每次运行中,最后一步(ending wait loop 0x1f890670->0x0都没有发生)。因此,等待循环永远不会结束。有时代码崩溃,恰好在同一点。任何线索在这里有什么不妥。

    我的代码如下:

        @implementation SAMCommand {
        NSData* resultData;
        dispatch_semaphore_t semaphore; // a lock to establish whether the command has been processed
    }
    
    // construct the object, ensuring that the "command" field is present in the jsonString
    +(NSData*) createAndExecuteCommandWithJSONParamsAs:(NSString *)jsonString {
    
        SAMCommand* samCommand = [[SAMCommand alloc] init];
    
        samCommand.commandParams = [jsonString dictionaryFromJSON];
    
        if(COMPONENT==nil || COMMAND==nil){
            DDLogError(@"command not found in %@",jsonString);
            return nil;
        }
    
        samCommand->semaphore = dispatch_semaphore_create(0);
        DDLogInfo(@"created semaphore %p->%p",samCommand,samCommand->semaphore);
    
        // to execute a command contained in the jsonString, we use reflection. 
        DDLogInfo(@"calling execute with %p",samCommand);
        [NSClassFromString(COMPONENT) performSelectorOnMainThread:NSSelectorFromString([NSString stringWithFormat:@"%@_%@_%@:",COMMAND,MEDIA_SOURCE,MEDIA_TYPE]) withObject:samCommand waitUntilDone:NO];
    
        // the above calls are non-blocking. hence, wait in a loop until the command has finished
        DDLogInfo(@"starting wait loop %p->%p",samCommand,samCommand->semaphore);
        dispatch_semaphore_wait(samCommand->semaphore, DISPATCH_TIME_FOREVER);
        DDLogInfo(@"ending wait loop %p->%p",samCommand,samCommand->semaphore);
        DDLogInfo(@"");
    
        // return the data
        return samCommand->resultData;
    
    }
    
    // to be called at the end of an asynch operation (eg: reading local asset list)
    - (void) commandDidFinish {
    
        // flag to release the lock
        DDLogInfo(@"releasing semaphore %p->%p",self,semaphore);
        dispatch_semaphore_signal(semaphore);
        semaphore = nil;
    
    }
    
    @end
    

    我得到了它的工作:)

    最后,似乎运行稳定的是创建信号量,并将其传递给ALAsset异步API调用,并在调用结束时释放它。之前,我正在调用我创建信号量的类的委托方法,并且信号量对象以某种方式获得释放。不确定那里发生了什么。

2 个答案:

答案 0 :(得分:0)

NSRunLoop有一个名为runUntilDate的方法:它应该适用于你在不久的将来提前1个左右的日期。这样你就可以在while循环中替换你的sleep调用,例如:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeintervalSinveNow:1]

答案 1 :(得分:0)

您可以使用信号量阻止当前队列的执行,直到另一个队列返回 基本模式是:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[assetsLibrary enumerateAssetsUsingBlock^(ALAsset *result, NSUInteger index, BOOL *stop):^{
    ...
    dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);

您可以在Apple的MTAudioProcessingTap Xcode项目中找到此方法的完整示例: https://developer.apple.com/library/ios/samplecode/AudioTapProcessor
相关的行从MYViewController.m开始:86