2011-04-23 256 views
1

我在写一个Objective-C类,我想要线程安全。要做到这一点,我使用pthreads和pthread_rwlock(使用@synchronized是矫枉过正,我想了解更多关于pthreads)。该锁定在指定为init方法的对象中,并在dealloc中销毁。我有三种操作锁定的方法; readLockwriteLockunlock。这三个方法只是简单地调用相关的pthread函数,目前没有其他的东西。如何确定线程是否有锁?

下面是的对象的方法,两者都需要一个writeLock二:

-(void)addValue:(const void *)buffer 
{ 
    [self writeLock]; 

    NSUInteger lastIndex = self.lastIndex; 
    [self setValue:buffer atIndex:(lastIndex == NSNotFound) ? 0 : lastIndex+1]; 

    [self unlock]; 
} 


-(void)setValue:(const void *)buffer atIndex:(NSUInteger)index 
{ 
    [self writeLock]; 
    //do work here 
    [self unlock]; 
} 

setAddValue:调用将首先获得写入锁定,然后调用setValue:atIndex:这也将试图获得一个写锁。该文档指出,发生这种情况时行为是不确定的。因此,如何在尝试获取锁之前检查线程是否有锁? (我可以确保临界区不会触发另一个锁请求,但这意味着代码重复,并且我想让我的代码干掉)。

回答

2

不完全清楚你正在使用什么样的锁。你指出你正在使用pthreads,并且读/写锁,所以我得出结论说你正在使用pthread_rwlock。

如果是这样,那么你应该可以在锁上使用pthread_rwlock_trywrlock。从手册页开始,

如果成功,则pthread_rwlock_wrlock()和pthread_rwlock_trywrlock()将返回零。否则,将返回一个错误号码 以表明错误。

而且,一个错误的是:

[EDEADLK]调用线程已经拥有读/写锁定 (读或写)。

因此,我认为你应该能够调用pthread_rwlock_trywrlock(),你要么是成功的,它会返回EBUSY如果另一个线程已锁定,或者你会得到EDEADLK如果当前线程具有锁。

+0

该信息稍微正确。 'EDEADLK'错误只针对'pthread_rwlock_(rd | wr)锁定'指定,而不是trylock函数,在这种情况下它将返回'EBUSY',并且不区分锁定是由调用线程还是其他线程持有。此外,由同一个线程保持的锁只有在写入锁定时才能检测到。由于可以有无限数量的阅读器,所以持有锁的阅读器的身份不能被检查。然而,在OP的情况下,它看起来像一个双重写锁是需要避免的问题,所以它可能是好的。 – 2011-04-23 21:25:05

+0

此外,'EDEADLK'错误只是一个“可能”,而不是“应该”,即不需要执行检测和报告 – 2011-04-23 21:26:45

+0

在进一步的考虑中,我认为您的解决方案不可行。我已经发布了一个替代方案作为答案。 – 2011-04-23 21:32:44

0

首先,仅包含一个操作的关键部分是无用的。关键在于使不同的事物相互同步。 (你确实有效地使整数原子,但这可能不是完整的意图。)

其次,你已经知道你在后面的关键部分有写锁定,所以没有必要检查它是否存在或不。写作时不要尝试读锁。

解决方案可能会将readLockwriteLock调用转移到调用函数中,但不知道更多情况是不可能的。

(这也可能会通过减少总的操作次数,你将不会被锁定,然后立即解锁减少锁定的性能开销。也许你并不需要直接在并行线程级的工作。)

+0

第一句话是危险错误的。在C中没有一个单一的原子操作。如果你正在编写x86汇编,并且使用带有'lock'前缀的内存操作指令,那么你会是正确的,但是否则你需要锁定。 – 2011-04-23 21:34:24

+0

@R ..:我说他*锁定的效果是使整数原子。或者至少,他*是做的。自从我写回答以来,问题发生了变化(奇怪的是,自从他接受了另一个答案以来)。 – Potatoswatter 2011-04-23 21:56:36

0

可移植程序不能依赖实现来告诉调用者它已经拥有写入锁定。相反,你需要做这样的事情来包装rwlocks用递归写锁:

int wrlock_wrap(pthread_rwlock_t *l, int *cnt) 
{ 
    int r = *cnt ? 0 : pthread_rwlock_wrlocK(l); 
    if (!r) ++*cnt; 
    return r; 
} 

int wrunlock_wrap(pthread_rwlock_t *l, int *cnt) 
{ 
    --*cnt; 
    return pthread_rwlock_unlock(l); 
} 

你可以保持pthread_rwlock_t旁边的计数无论它的存储,例如作为你的struct/class/whatever的成员。

+0

关于可移植性的说明是正确的,但OP显然在OS X上,并且在Objective C和Cocoa框架的选择中放弃了可移植性考虑。我最初考虑的是类似于你的建议的实现(尽管直接在OP显示的实现内部直接实现),但在检查OS X手册页(参见上面的参考资料)后放弃了它。 – 2011-04-24 02:01:33