atomic 与 nonatomic

来源:互联网 发布:c语言a|b 编辑:程序博客网 时间:2024/05/22 13:19

在多进程(线程)访问共享资源时,能够确保所有其他的进程(线程)都不在同一时间内访问相同的资源。原子操作(atomic operation)是不需要synchronized,这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

原子性:
1. 原子性内部也有一把,自旋锁
2. 效率比互斥要高

跟互斥锁区别
1. 互斥锁在等待的时候,会休眠,只要锁一开,马上唤醒,执行代码
2. 自旋锁在等待的时候,一直在判断(死循环) ,只要锁一开,马上执行

// iOS 开发中,所有的属性都应该使用 非原子性 nonatomic
// 所有的 UI 都是 非线程安全的,所以所有的UI操作都要在主线程上执行。
// 如果在子线程执行,有可能程序崩了(机率很大),也有可能不及时刷新

原子和非原子属性
OC 在定义属性的时候有nonatomic和atomic两种选择
atomic:原子属性,为 setter 方法加锁
nonatomic:非原子属性,不会为 setter 方法加锁
普通情况下都是在主线程做操作,所以一般都不会加锁。

对比

atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备
synchronized 与 atomic
synchronized:互斥锁
atomic:自旋锁

共同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。

区别:

互斥锁:当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会进入睡眠状态等待任务执行完毕,当上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务。

自旋锁:当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行。

自旋锁应用场景:
比较适合做一些不耗时的操作,自旋锁的效率高于互斥锁。

IOS开发建议

所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减少移动客户端的压力

一般iOS程序中,所有属性都声明为nonatomic。这样做的原因是:
在iOS中使用同步锁的开销比较大, 这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”(thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。

atomic一定是线程安全的么,回答是NO :

nonatomic的内存管理语义是非原子性的,非原子性的操作本来就是线程不安全,而atomic的操作是原子性的,但并不意味着他就是线程安全的,它会增加正确的几率,能够更好的避免线程错误,但仍旧是不安全的。

为了说atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同:

nonatomic的实现:
- (void)setCurrentImage:(UIImage *)currentImage
{
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];

    // do something}

}
- (UIImage *)currentImage
{
return _currentImage;
}

atomic的实现:
- (void)setCurrentImage:(UIImage *)currentImage
{
@synchronized(self) {
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];

        // do something    }}

}

  • (UIImage *)currentImage
    {
    @synchronized(self) {
    return _currentImage;
    }
    }

当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为等该操作而等待。当A线程的写操作结束后,B线程进行写操作,然后当A线程需要读操作时,却获得了在B线程的值,这就破坏了线程安全,如果有线程C在A线程读操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。(atomic修饰block时,就不一定安全,参考https://www.mgenware.com/blog/?p=1493)

其实无论是否是原子性的只是针对于getter和setter而言,比如用atomic去操作一个NSMutableArray ,如果一个线程循环读数据,一个线程循环写数据,肯定会产生内存问题,这个就跟getter和setter就木有关系了。

http://www.parallellabs.com/2011/04/09/pthread-mutex-lock-and-thread-safety/

0 0
原创粉丝点击