关于使这个线程安全但高效的建议?

时间:2013-01-10 06:14:20

标签: ios objective-c multithreading macos grand-central-dispatch

我一直在考虑一个看起来很容易实现的问题,但是一个有效且线程安全的解决方案正在阻碍我。我想要做的是创建一些工作对象。几个呼叫者可能会要求它从不同的线程工作。要求是请求不得排队。换句话说,如果有人要求工人做工作但看到它已经在工作,那么应该尽早回来。

这是一个简单的第一步:

@interface Worker : NSObject
@property (nonatomic, assign, getter = isWorking) BOOL working;
- (void)doWork;
@end

@implementation Worker
{
    dispatch_queue_t _workerQueue; //... a private serial queue
}

- (void)doWork
{
    if ( self.isWorking )
    {
        return;
    }
    self.working = YES;
    dispatch_async(_workerQueue, ^{
        // Do time consuming work here ... Done!
        self.working = NO;
    });
}
@end

这个问题是isWorking属性不是线程安全的。将其标记为原子在这里将无济于事,因为对它的访问需要在几个语句中同步。

为了使其成为线程安全,我需要使用锁来保护isWorking:

@interface Worker : NSObject
@property (nonatomic, assign, getter = isWorking) BOOL working;
- (void)doWork;
@end

@implementation Worker
{
    dispatch_queue_t _workerQueue; //... a private serial queue
    NSLock *_lock; // assume this is created
}

- (void)doWork
{
    [_lock lock];
    if ( self.isWorking )
    {
        [_lock unlock];
        return;
    }
    self.working = YES;
    [_lock unlock];
    dispatch_async(_workerQueue, ^{
        // Do time consuming work here ... Done!
        [_lock lock];
        self.working = NO;
        [_lock unlock];
    });
}

@end

虽然我确实相信这会是线程安全的,但我认为必须经常采取并放弃锁定(昂贵的操作)是非常糟糕的。

那么,有更优雅的解决方案吗?

3 个答案:

答案 0 :(得分:3)

dispatch_semaphore is the idiomatic way to limit access to a finite resource,如果您已经在使用GCD。

// Add an ivar:
dispatch_semaphore_t _semaphore;

// To initialize:
_semaphore = dispatch_semaphore_create(1);

// To "do work" from any thread:
- (void)doWork
{
     if (dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_NOW) == 0) {
         // We got the semaphore without waiting, so we're first in line.
         dispatch_async(_workerQueue, ^{
             // do time consuming work here, then when done:
             dispatch_semaphore_signal(_semaphore);
         });
     } else {
         // We would have had to wait for the semaphore, so somebody must have
         // been doing work already, and we should do nothing.
     }
}

Here's a blog post explaining in more detail.

答案 1 :(得分:2)

您可以在此处使用原子test-and-set操作。 GCC为此目的提供__atomic_test_and_set。以下是您在C(未经测试)中使用它的方法:

static volatile bool working = FALSE;
if(__atomic_test_and_set(&working, __ATOMIC_ACQUIRE)) {
    // Already was working.
}else{
    // Do work, possibly in another thread.
    // When done:
    __atomic_clear(&working, __ATOMIC_RELEASE);
}

简单,是吗?

答案 2 :(得分:0)

为了使属性成为线程安全的,你可以简单地使用@synchronize。