使用 NSRecursive Lock 等待操作完成

发布于 2024-10-27 20:22:39 字数 1071 浏览 2 评论 0原文

使用 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 的值是否是线程安全的?如果在后台线程仍在加载时调用同步函数,我还会在同步函数中锁定两次。

有没有更通用、更好的方法来实现这个功能?

谢谢!

Would it be save to use an NSRecursiveLock to purposefully wait for an operation to complete on a background thread? Here is an example:

I have a class where I want to have a loadParts function that can be asynchronous or synchronous. The asynchronous function will possibly be called early so that the parts can be loaded before the data is actually needed. The synchronous one should check if the data has been loaded, or is being loaded currently. If it has been loaded it can just return the data; if it is currently loaded, then it should wait for it to be loaded and then return; and if it isn't even being loaded, then it should just load it synchronously. This is the code I am trying to use:

// 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];
    }
}

Is this safe / an efficient way to do this? I already see some possible problems with it. Is it thread safe to set and test the value of a BOOL without a lock? I am also locking twice in the synchronous function if it is called while the background thread is still loading.

Is there a more common and better way to achieve this functionality?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

往日情怀 2024-11-03 20:22:39

一个更好的解决方案是使用dispatch_queue或NSOperationQueue(配置为串行操作)。

将加载操作排队,然后将完成后应该发生的任何事情排队。如果“完成”操作是“告诉主线程更新”,那很好——在主线程上执行一个方法,该方法实际上是一个触发更新以响应现在加载的数据的事件。

这完全避免了与锁定相关的问题和开销,同时还解决了“完成”通知问题,而不需要某种轮询机制。

A far, far, better solution is to use a dispatch_queue or NSOperationQueue (configured for serial operation).

Enqueue your loading operations and then enqueue whatever is supposed to happen when it is done. If the "done" operation is "tell the main thread to update", that's fine -- perform a method on your main thread that is effectively an event that triggers the update in response to the now loaded data.

This avoids the issues and overhead associated with locking entirely while also solving the "is done" notification issue without requiring some kind of polling mechanism.

月依秋水 2024-11-03 20:22:39

受到 bbum 的回答的启发,我找到了一个使用 NSOperationQueue 的解决方案,但并不完全按照他描述的方式。在我的 preloadParts 函数中,我创建并存储一个加载操作,它是运行我的后台线程函数的 NSInitationOperation 的实例。然后我将其添加到 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];

Inspired by bbum's answer I found a solution that uses NSOperationQueue but not quite in the way he described. In my preloadParts function I create and store a load operation that is an instance of NSInvocationOperation that runs my background thread function. I then add it to the NSOperationQueue.

If at any point, the data is requested by another class. I first check if the data is loaded (the variable is set). If not, I check if the operation is in the queue. If it is, then I call [_loadOperation waitUntilFinished]. Otherwise, I add it to the operation queue with the argument to waitUntilFinished. Here is the code I came up with:

-(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];
}

In the initialization function I set the _operationQueue and _loadOperation as follows:

_operationQueue = [[NSOperationQueue alloc] init];
            _loadOperation = [[NSInvocationOperation alloc] initWithTarget:self 
                                                                  selector:@selector(_loadCategories) 
                                                                    object:nil];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文