我一直在考虑一个看起来很容易实现的问题,但是一个有效且线程安全的解决方案正在阻碍我。我想要做的是创建一些工作对象。几个呼叫者可能会要求它从不同的线程工作。要求是请求不得排队。换句话说,如果有人要求工人做工作但看到它已经在工作,那么应该尽早回来。
这是一个简单的第一步:
@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
虽然我确实相信这会是线程安全的,但我认为必须经常采取并放弃锁定(昂贵的操作)是非常糟糕的。
那么,有更优雅的解决方案吗?
答案 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.
}
}
答案 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。