多线程数据安全

来源:互联网 发布:linux dd命令备份iso 编辑:程序博客网 时间:2024/06/05 04:59

1.什么是线程安全?

线程安全是指多线程同时访问一块资源造成的数据安全问题。


2.解决线程安全问题的几种方式:

OSSpinLock(自旋锁)

dispatch_semaphore(信号量)

pthread_mutex(互斥锁)

NSLock (内部封装了一个pthread_mutex)

NSCondition (条件锁)

NSRecursiveLock (递归锁)

NSConditionLock (借助 NSCondition 实现)

synchronized (关键字加锁)


3.几种方式的简单使用

(1)OSSpinLock 

   自旋锁的实现原理比较简单,就是死循环。当a线程获得锁以后,b线程想要获取锁就需要等待a线程释放锁。在没有获得锁期间,b线程会一直处于忙等的状态。如果a线程在临界区的执行时间过长,则b线程会消耗大量的cpu时间,不太划算。所以自旋锁在临界区执行时间比较短的环境效率比较高。


 使用方法:需要导入 import <libkern/OSAtomic.h> 头文件

                OSSpinLock lock = OS_SPINLOCK_INIT;

                OSSpinLockLock(&lock); //加锁

                需要加锁代码块

                OSSpinLockUnlock(&lock);//解锁



(2)dispatch_semaphore

          信号量:实现原理跟自旋锁有点不一样。它首先将信号量减一,并判断是否大于等于0,如果是则返回0,并继续执行后续代码,否则,使线程进入睡眠状态,让出cpu时间。直到信号量大于0或者超出时,则线程会被重新唤醒执行后续代码。

        

 使用方法:dispatch_semaphore_t semaphore = dispatch_semaphore_creare(1);// 创建信号量

                dispatch_semaphore_wait(semaphore);//判断信号量是否大于0,并使信号量减一

                需要加锁的代码

                dispatch_semaphore_signal(semaphore); //信号量加一

(3)pthread_mutex

        互斥锁:和信号量的实现原理类似,也是阻塞线程并进入睡眠,需要进行上下文切换

        

  使用方法:需要导入 import<ptheard.h>

                pthread_mutexattr_t attr;

                pthread_mutexattr_init(&attr);

                pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);

                pthread_mutex_t lock;

                pthread_mutex_init(&lock,&attr); //设置属性

                pthread_mutex_lock(&lock); //上锁

                需要执行代码块

                pthread_mutex_unlock(&lock);//解锁



(4)NSLock

        锁:NSLock在内部封装了一个 pthread_mutex,属性为 PTHREAD_MUTEX_ERRORCHECK

   

 使用方法:NSLock* lock = [[NSLock alloc] init];//锁对象

                 [lock lock];//加锁

                  需要执行的代码

                 [lock unlock];

(5)NSCondition

        条件锁:封装了一个互斥锁和条件变量。互斥锁保证线程安全,条件变量保证执行顺序。

 

 使用方法:NSCondition* lock = [[NSCondition alloc] init];

                [lock lock];//加锁

                需要执行的代码

                [lock unlock];//解锁


(6)NSRecursiveLock

        递归锁:pthread_mutex(recursive)的封装


 使用方法:NSRecursiveLock* lock = [[NSRecursive alloc]init];

                [lock lock];//加锁

                需要执行代码块

                [lock unlock];

         

(7)NSConditionLock

        条件锁:借助 NSCondition 实现,本质是生产-消费者模型

 

 使用方法:NSConditonLock* lock = [[NSConditionLock alloc] init];

                 [lock lock];//加锁

                  需要执行的代码块

                  [lock unlock];



(8)synchronized

        关键字加锁:一个对象层面的加锁,锁住了整个对象,底层使用了,互斥递归锁来实现。

    

 使用方法:@sychronized(锁住对象){需要锁的代码};注意:锁定一份代码,只能用一把锁,用多把锁是无效的。



4.性能对比

对以上各个锁进行1000000此的加锁解锁的空操作时间如下:

OSSpinLock: 46.15 ms
dispatch_semaphore: 56.50 ms
pthread_mutex: 178.28 ms
NSCondition: 193.38 ms
NSLock: 175.02 ms
pthread_mutex(recursive): 172.56 ms
NSRecursiveLock: 157.44 ms
NSConditionLock: 490.04 ms
@synchronized: 371.17 ms

总结

OSSpinLock和dispatch_semaphore的效率远远高于其他。

@synchronized和NSConditionLock效率较差。

鉴于OSSpinLock的不安全,所以我们在开发中如果考虑性能的话,建议使用dispatch_semaphore。

如果不考虑性能,只是图个方便的话,那就使用@synchronized。




原创粉丝点击