使用NSRecursiveLock故意等待后台线程上的操作完成是否可以保存?这是一个例子:
我有一个类,我希望有一个可以异步或同步的loadParts函数。异步函数可能会提前调用,以便在实际需要数据之前加载这些部分。同步应该检查数据是否已加载,或者当前是否正在加载。如果它已被加载,它只能返回数据;如果它当前已加载,那么它应该等待它被加载然后返回;如果它甚至没有加载,那么它应该只是同步加载它。这是我尝试使用的代码:
// Private function to be run either on main thread or
// background thread
-(void)_loadParts
{
[_loadingPartsLock lock];
_loadingParts = YES;
// Do long loading operation
_loadingParts = NO;
_partsLoaded = YES;
[_loadingPartsLock unlock];
}
// Asynchronous loading of parts
-(void)preloadParts
{
if( _loadingParts || _partsLoaded )
return;
[self performSelectorInBackground:@selector(_loadParts) withObject:nil];
}
// Synchronous loading of parts
-(void)loadParts
{
if( _loadingParts )
{
[_loadingPartsLock lock];
[_loadingPartsLock unlock];
}
if( !_partsLoaded )
{
[self _loadParts];
}
}
这样做是否安全/有效?我已经看到了一些可能的问题。在没有锁定的情况下设置和测试BOOL的值是否可以安全线程?如果在后台线程仍在加载时调用它,我也会在同步函数中锁定两次。
是否有更常见,更好的方法来实现此功能?
谢谢!
答案 0 :(得分:3)
远远更好的解决方案是使用dispatch_queue
或NSOperationQueue
(配置为串行操作)。
将您的加载操作排入队列,然后将完成后应该发生的任何事情排入队列。如果“完成”操作是“告诉主线程更新”,那很好 - 在主线程上执行一个方法,该方法实际上是一个触发更新以响应当前加载数据的事件。
这样可以避免与完全锁定相关的问题和开销,同时解决“已完成”通知问题,而无需某种轮询机制。
答案 1 :(得分:0)
受bbum的回答启发,我找到了一个使用NSOperationQueue的解决方案,但并不像他描述的那样。在我的preloadParts函数中,我创建并存储一个加载操作,该操作是运行我的后台线程函数的NSInvocationOperation实例。然后我将它添加到NSOperationQueue。
如果在任何时候,数据是由另一个类请求的。我首先检查数据是否已加载(变量已设置)。如果没有,我检查操作是否在队列中。如果是,那么我调用[_loadOperation waitUntilFinished]。否则,我将其添加到具有waitUntilFinished参数的操作队列中。这是我提出的代码:
-(void)preloadCategories
{
if( [[_operationQueue operations] containsObject:_loadOperation] )
return;
[_operationQueue addOperation:_loadOperation];
}
-(CCPart*)getCategoryForName:(NSString*)name
{
if( nil == _parts )
{
[self loadCategories];
}
return [_parts objectForKey:name];
}
-(void)loadCategories
{
if( nil != _parts )
return;
if( [[_operationQueue operations] containsObject:_loadOperation] )
{
[_loadOperation waitUntilFinished];
}
else
{
[_operationQueue addOperations:[NSArray arrayWithObject:_loadOperation]
waitUntilFinished:YES];
}
}
-(void)_loadCategories
{
// Function that actually does the loading and sets _parts to be an array of the data
_parts = [NSArray array];
}
在初始化函数中,我按如下方式设置_operationQueue和_loadOperation:
_operationQueue = [[NSOperationQueue alloc] init];
_loadOperation = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(_loadCategories)
object:nil];