在 Objective-C 中,使用 @property (atomic) 时,底层实现的线程同步是通过一种称为**"锁"**的机制来确保原子性,防止多个线程同时访问或修改同一属性。具体来说,@property (atomic) 的底层实现通常使用 互斥锁(mutex) 来保护 getter 和 setter 操作的原子性。
1. 底层锁的实现机制
-
互斥锁(Mutex) :为了确保对属性的访问是原子的,Objective-C 的 atomic 属性通常会在 getter 和 setter 方法中使用一个 互斥锁(mutex) 来保护对实例变量的访问。这个锁是线程间的同步工具,用来保证同一时刻只有一个线程能访问或修改某个变量,从而避免并发冲突。
- 当多个线程尝试同时调用一个
@property (atomic)的 getter 或 setter 方法时,系统会通过锁定访问对象的实例变量来确保访问的原子性。锁会防止其他线程在当前线程操作时进行干扰。
- 当多个线程尝试同时调用一个
2. 实现细节
在大多数实现中,@property (atomic) 背后用的可能是一个 OS spin lock 或 pthread_mutex。具体实现可能依赖于操作系统和编译器的不同,但常见的底层实现是:
- OS spin lock(自旋锁):自旋锁是一种锁机制,线程在获取锁之前会忙等待(即循环等待)。这种锁的优点是开销较小,但如果锁竞争激烈,可能导致 CPU 资源浪费。
- pthread_mutex:
pthread_mutex是 POSIX 线程库提供的互斥锁,它可以用于线程间同步,确保只有一个线程可以访问共享资源。大多数系统会使用这种锁来保证同步操作的原子性。
3. 如何工作
以 @property (atomic) 的 setter 为例:
- 当一个线程调用
setter方法时,它会试图获取锁。获取锁后,当前线程才能安全地修改对象的实例变量。 - 如果其他线程也在同时调用该
setter方法,后来的线程会被阻塞,直到第一个线程释放锁。 - 一旦第一个线程完成操作并释放锁,其他线程可以获取锁并继续执行。
4. 为什么 atomic 不能确保线程安全
虽然 atomic 保证了每次访问属性时的原子性,但它并没有解决多个操作之间的同步问题。因此,即使底层使用了锁,多个线程同时调用一个复合操作(比如读取、修改、再写回)时仍然可能导致数据不一致。这是因为 atomic 锁只是对单个 getter 或 setter 操作进行保护,但对于类似 self.value = self.value + 1 这样包含多次操作的复合操作,它依然不能提供线程安全保障。
5. 锁的粒度
@property (atomic) 使用的锁通常是基于 实例变量(instance variable)的锁,即每个对象的每个属性都会有一个独立的锁。这意味着同一个对象的多个属性之间是互相独立的,不会互相影响。但如果多个线程操作同一个属性,它们之间会通过互斥锁来串行化执行,确保同一时间只有一个线程能够访问该属性。
6. 总结
@property (atomic)通过互斥锁(如 pthread_mutex 或 OS spin lock)确保对属性的原子性操作。- 这种锁机制只对单一的
getter或setter方法提供原子性保障,不能保证多步骤复合操作的线程安全。 - 虽然底层通过锁保护了属性的读写,但如果需要更复杂的线程同步(如多个属性之间的协调),还需要开发者手动实现额外的同步机制(如使用
@synchronized或其他锁机制)。