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

Sve*_*ven 5 macos cocoa objective-c thread-safety

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

为了节省一些内存和时间比较,我只保留a中的唯一实例,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;
}
Run Code Online (Sandbox Code Playgroud)

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

这工作正常,峰值内存使用率显着降低,我的所有测试用例仍然通过.但我不太确定我是否可以依靠指针比较来测试相等性.是否有任何情况或竞争条件,我同时得到两个不同的实例(所以指针是不同的)但他们仍然比较相等isEqual:

澄清我有/想要的东西:

特定

MyClass *a = [[MyClass alloc] initWithStuff: stringA];
MyClass *b = [[MyClass alloc] initWithStuff: stringB];
Run Code Online (Sandbox Code Playgroud)

我们一直都有

[a isEqual: b] == [stringA isEqual: stringB];
Run Code Online (Sandbox Code Playgroud)

新代码不会改变这一点.

我想要实现的目标也是

[a isEqual: b] == (a == b)
Run Code Online (Sandbox Code Playgroud)

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

对于单线程代码或者如果我使用的是NSMutableSet代替弱代,那么NSHashTable总是可以解决.我只是不确定是否有任何竞争条件,所以我可以在这种情况下

[a isEqual: b] && (a != b)
Run Code Online (Sandbox Code Playgroud)

是真的.

bbu*_*bum 4

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

如果可能,将唯一代码移至类 [工厂] 方法并在 的值上唯一stuff。这可能是伪代码,但它似乎stuff是初始化状态的唯一来源。无论如何,这样做会在碰撞[缓存]情况下保存通过alloc/ 。dealloc

假设没有其他使用UniquingHashTable(),将staticthat =dispatch_once()初始化器的声明移动到您的initWithStuff:(或工厂)方法中。它使代码更清晰且更不易脆弱(因为没有任何东西可以从外部破坏哈希表)。

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

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


的实现isEqual:通常首先测试指针相等性,只有在失败时,它才会进入测试内部状态所需的兔子洞以确保相等性。

如果你有一个习惯isEqual:,它也应该做同样的事情。

在冲突情况下,您的代码(如最初实现的那样)已经产生了 amallocfree(通过+alloc和)的开销。-dealloc与几次调用相比,这似乎是一个巨大的开销isEqual:(假设您的哈希表的冲突并不严重)。

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