0

我一直在思考一个看似很容易实现的问题,但一个高效且线程安全的解决方案却让我感到困扰。我想做的是创建某种工作对象。几个调用者可能会要求它从不同的线程工作。一个要求是请求不能排队。换句话说,如果有人要求工人工作,但看到它已经在工作,它应该早点回来。

一个简单的第一关是这样的:

@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];
    });
}

@结尾

虽然我确实相信这将是线程安全的,但我认为不得不如此频繁地获取和放弃锁(一项昂贵的操作)是非常糟糕的。

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

4

3 回答 3

3

dispatch_semaphore如果您已经在使用 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.
     }
}

这是一篇更详细解释的博客文章。

于 2013-01-10T06:33:43.517 回答
2

您可以在此处使用原子测试和设置操作。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);
}

容易吧?

于 2013-01-10T06:20:10.590 回答
0

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

于 2013-01-10T06:22:19.373 回答