我的unquing代码的线程安全性如何?

时间:2013-04-01 15:34:41

标签: objective-c macos cocoa thread-safety

我有一个小值类,我创建了很多实例。通常具有相同的价值。此类用作一种标识符,因此主要用途是将此类的实例相互比较(通过isEqual:)。

为了节省一些内存和时间比较,我只保留NSHashTable中的唯一实例,并使用指针比较代替isEqual:

所以我指定的初始化程序如下所示:

- initWithStuff: (NSString *)stuff;
{
    self = [super init];
    if (!self) return nil;

    // ... (actual initialisation code omitted)

   hash = UniquingHashTable();

   static OSSpinLock spinlock = OS_SPINLOCK_INIT;
   OSSpinLockLock( &spinlock );

   id member = [hash member: self];
   if (member) self = member;
   else [hash addObject: self];

   OSSpinLockUnlock( &spinlock );

   return self;
}

UniquingHashTable()返回全局NSHashTable或通过[NSHashTable weakObjectsHashTable]创建它(如果它尚不存在)。 weakObjectsHashTable是重要的一点 - 它存储了对象的弱指针,一旦没有对该对象的其他强引用,它就会被自动删除。由于dispatch_once,初始化是线程安全的。

这很好用,峰值内存使用率显着降低,我的所有测试用例仍然通过。但我不太确定我是否可以依靠指针比较来测试相等性。 是否存在任何情况或竞争条件,我同时获得两个不同的实例(因此指针不同)但它们仍然比较等于isEqual:

澄清我有/想要的东西:

鉴于

MyClass *a = [[MyClass alloc] initWithStuff: stringA];
MyClass *b = [[MyClass alloc] initWithStuff: stringB];

我们总是有

[a isEqual: b] == [stringA isEqual: stringB];

新代码没有改变。

我想要实现的目标也是

[a isEqual: b] == (a == b)

这样我就可以用更快的指针比较替换isEqual:。 (是的,我测量了这一点,这将是重要的)。

对于单线程代码或者如果我使用的是NSMutableSet而不是弱NSHashTable,这总是有效的。我只是不确定是否有任何竞争条件,所以我可以有

的情况
[a isEqual: b] && (a != b)

是真的。

1 个答案:

答案 0 :(得分:4)

该代码应该没问题,但我会建议一些可能的更改。这些是基于假设/观察,可能适用也可能不适用。

如果可能,将唯一代码移动到类[factory]方法,并将stuff的值唯一。这可能是伪代码,但似乎stuff是初始化状态的唯一来源。无论如何,这样做会在碰撞[缓存]案例中保存alloc / dealloc的传递。

假设没有其他内容使用UniquingHashTable(),请将static初始化程序的dispatch_once()声明移到initWithStuff:(或工厂)方法中。它使代码更清晰,更不易碎(因为没有任何东西可以在外部使用哈希表)。

使用串行GCD队列在缓存查找代码中进行序列化;队列非常便宜,而且通常不必越过用户 - >内核屏障来排队和执行一个块。

哈希表将使用hashisEqual:的组合来准确地确定对象相等性(实现细节,实际上......因为弱哈希表使用了对象个性,isEqual:将被用作决定平等的最终决定权)。因此,哈希冲突应该不是问题。如果要确保不同地址的两个对象不相等,请相应地实现isEqual:(两个相等的对象必须具有相同的对象) hash,两个不等的对象可能具有相同的哈希值。)


isEqual:的实现通常首先测试指针相等性,并且只有在失败的情况下,它才会在测试内部状态的必要兔洞中进行测试,以确保相等。

如果您有自定义isEqual:,则应该这样做。

您的代码(最初实施的)已经在碰撞案例中产生了mallocfree(通过+alloc-dealloc)的开销。与isEqual:的一些调用相比,这将是一个看似巨大的开销(假设您的哈希表不是非常大的冲突)。

您如何衡量isEqual:与指针比较的开销?