* 原子操作
nonatomic: 非原子属性, 非线程安全, 适合小内存移动设备
atomic: 原子属性, default, 线程安全(内部使用自旋锁), 消耗大量资源
单写多读, 只为 setter 方法加锁, 不影响 getter
相关代码如下:
- static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
- {
- if (offset == 0) {
- object_setClass(self, newValue);
- return;
- }
- id oldValue;
- id *slot = (id*) ((char*)self + offset);
- if (copy) {
- newValue = [newValue copyWithZone:nil];
- } else if (mutableCopy) {
- newValue = [newValue mutableCopyWithZone:nil];
- } else {
- if (*slot == newValue) return;
- newValue = objc_retain(newValue);
- }
- if (!atomic) {
- oldValue = *slot;
- *slot = newValue;
- } else {
- spinlock_t& slotlock = PropertyLocks[slot];
- slotlock.lock();
- oldValue = *slot;
- *slot = newValue;
- slotlock.unlock();
- }
- objc_release(oldValue);
- }
- void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
- {
- bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
- bool mutableCopy = (shouldCopy == MUTABLE_COPY);
- reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
- }
总结: 很容易理解的代码, 可变拷贝和不可变拷贝会开辟新的空间, 两者皆不是则持有(引用计数 + 1), 相比 nonatomic 只是多了一步锁操作.
* 使用示例
- #import "ViewController.h"
- #import "Person.h"
- #import <libkern/OSAtomic.h>
- @interface ViewController ()
- @end
- @implementation ViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- [self useLock];
- }
- - (void)withoutLock {
- __block Person *p = [[Person alloc]init];
- [NSThread detachNewThreadWithBlock:^{
- for (int i = 0; i<1000; i++) {
- p.age ++;
- }
- NSLog(@"%zd\n",p.age);
- }];
- [NSThread detachNewThreadWithBlock:^{
- @synchronized(self){
- }
- for (int i = 0; i<1000; i++) {
- p.age ++;
- }
- NSLog(@"%zd\n",p.age);
- }];
- }
- - (void)useLock {
- __block OSSpinLock spinLock = OS_SPINLOCK_INIT; // 创建锁
- __block Person *p = [[Person alloc]init];
- NSLog(@"begin:");
- [NSThread detachNewThreadWithBlock:^{
- for (int i = 0; i<1000; i++) {
- OSSpinLockLock(&spinLock); // 加锁
- p.age ++;
- OSSpinLockLock(&spinLock); // 解锁
- }
- NSLog(@"%zd\n",p.age);
- }];
- [NSThread detachNewThreadWithBlock:^{
- for (int i = 0; i<1000; i++) {
- OSSpinLockLock(&spinLock); // 加锁
- p.age ++;
- OSSpinLockLock(&spinLock); // 解锁
- }
- NSLog(@"%zd\n",p.age);
- }];
- }
* 使用总结:
1)首先需要 #important<libkern/OSAtomic.h> , 因此关于自旋锁的 API 是在这个文件中声明的.
2)创建自旋锁也是通过一个静态宏, 在线程内通过 OSSpinLockLock 和 OSSpinLockUnlock 来上锁, 解锁. 如果不是因为现在的 OSSpinLock 出现了使用 bug, 在性能以及使用方面来说, 都是很好的使用锁的选择.
* 自旋锁的原理
就是 while 循环来占用 CPU, 实际上, 当 A 线程获取到锁时, CPU 会处于 while 死循环, 而这个死循环并不是 A 线程造成的, 当 A 获取到锁, 并且 B 线程也要申请锁时, 就会一直 while 循环询问 A 线程是否释放了该锁, 所以导致了 CPU 死循环, 因此是 B 线程导致的, 这个是 "自旋" 的由来, 正是因为这个一直等待询问, 并不类似于互斥锁, 互斥锁在申请时处于线程休眠状态, 所以才使自旋锁的性能高. 举个列子: 煮饭吃, 你的电饭锅 (A 线程) 正在煮饭 (资源), 而你本人(B 线程) 也想煮饭, 你有两种方式, 第一种, 一直在电饭锅前等待着, 看着饭好了没; 第二种, 去忙其它的, 每 15 分钟过来看一次饭好了没. 很显然, 按照第一种方式肯定是会先吃上饭.
Recursivelock 递归锁
* 需求场景:
一个锁只是请求一份资源, 而在一些开发实际中, 往往需要在代码中嵌套锁的使用, 也就是在同一个线程中, 一个锁还没有解锁就再次加锁. 这个时候就用到了递归锁.
* 实现原理:
递归锁也是通过 pthread_mutex_lock 函数来实现, 在函数内部会判断锁的类型. NSRecursiveLock 与 NSLock 的区别在于内部封装的 pthread_mutex_t 对象的类型不同, 前者的类型为 PTHREAD_MUTEX_RECURSIVE
* 运用场景:
循环(多张图片循环上传), 递归
* 使用示例:
示例一:
- // 递归锁实例化
- NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
- static void (^RecursiveMethod)(NSInteger);
- // 同一线程可多次加锁, 不会造成死锁
- RecursiveMethod = ^(NSInteger value){
- [lock lock];// 一进来就要开始加锁
- [NetWorkManager requestWithMethod:POST Url:url Parameters:paraDic success:^(id responseObject) {
- [self reuestForSuccess];
- // 一旦数据获取成功就要解锁 不然会造成死锁
- [lock unlock];
- } requestRrror:^(id requestRrror) {
- // 条件没有达到, 开始循环操作
- if(value> 0){
- RecursiveMethod(value-1);// 必须 - 1 循环
- }
- if(value == 0){
- // 条件 如果 == 0 代表循环的次数条件已经达到 可以做别的操作
- }
- // 失败后也要解锁
- [lock unlock];
- }];
- // 记得解锁
- [lock unlock];
- };
- // 设置递归锁循环次数 自定义
- RecursiveMethod(5);
示例二:
- - (void)recursiveLock {
- NSRecursiveLock *theLock = [[NSRecursiveLock alloc]init];
- [self MyRecursiveFucntion:5 recursiveLock:theLock];
- }
- - (void) MyRecursiveFucntion:(NSInteger )value recursiveLock:(NSRecursiveLock *)theLock {
- [theLock lock];
- if (value !=0) {
- --value;
- [self MyRecursiveFucntion:value recursiveLock:theLock];
- }
- [theLock unlock];
- }
Dispatch_semaphore 信号量
dispatch_semaphore 是 GCD 用来同步的一种方式, 与他相关的共有三个函数, 分别是
dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait.
下面我们逐一介绍三个函数:
(1)dispatch_semaphore_create 的声明为:
dispatch_semaphore_t dispatch_semaphore_create(long value);
传入的参数为 long, 输出一个 dispatch_semaphore_t 类型且值为 value 的信号量.
值得注意的是, 这里的传入的参数 value 必须大于或等于 0, 否则 dispatch_semaphore_create 会返回 NULL.
(关于信号量, 我就不在这里累述了, 网上很多介绍这个的. 我们这里主要讲一下 dispatch_semaphore 这三个函数的用法).
(2)dispatch_semaphore_signal 的声明为:
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
这个函数会使传入的信号量 dsema 的值加 1;(至于返回值, 待会儿再讲)
(3) dispatch_semaphore_wait 的声明为:
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
这个函数会使传入的信号量 dsema 的值减 1;
这个函数的作用是这样的, 如果 dsema 信号量的值大于 0, 该函数所处线程就继续执行下面的语句, 并且将信号量的值减 1;
如果 desema 的值为 0, 那么这个函数就阻塞当前线程等待 timeout(注意 timeout 的类型为 dispatch_time_t,
不能直接传入整形或 float 型数), 如果等待的期间 desema 的值被 dispatch_semaphore_signal 函数加 1 了,
且该函数 (即 dispatch_semaphore_wait) 所处线程获得了信号量, 那么就继续向下执行并将信号量减 1.
如果等待期间没有获取到信号量或者信号量的值一直为 0, 那么等到 timeout 时, 其所处线程自动执行其后语句.
(4)dispatch_semaphore_signal 的返回值为 long 类型, 当返回值为 0 时表示当前并没有线程等待其处理的信号量, 其处理
的信号量的值加 1 即可. 当返回值不为 0 时, 表示其当前有 (一个或多个) 线程等待其处理的信号量, 并且该函数唤醒了一
个等待的线程(当线程有优先级时, 唤醒优先级最高的线程; 否则随机唤醒).
dispatch_semaphore_wait 的返回值也为 long 型. 当其返回 0 时表示在 timeout 之前, 该函数所处的线程被成功唤醒.
当其返回不为 0 时, 表示 timeout 发生.
(5)在设置 timeout 时, 比较有用的两个宏: DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER.
DISPATCH_TIME_NOW 表示当前;
DISPATCH_TIME_FOREVER 表示遥远的未来;
一般可以直接设置 timeout 为这两个宏其中的一个, 或者自己创建一个 dispatch_time_t 类型的变量.
创建 dispatch_time_t 类型的变量有两种方法, dispatch_time 和 dispatch_walltime.
利用创建 dispatch_time 创建 dispatch_time_t 类型变量的时候一般也会用到这两个变量.
dispatch_time 的声明如下:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
其参数 when 需传入一个 dispatch_time_t 类型的变量, 和一个 delta 值. 表示 when 加 delta 时间就是 timeout 的时间.
例如: dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);
表示当前时间向后延时一秒为 timeout 的时间.
(6)关于信号量, 一般可以用停车来比喻.
停车场剩余 4 个车位, 那么即使同时来了四辆车也能停的下. 如果此时来了五辆车, 那么就有一辆需要等待.
信号量的值就相当于剩余车位的数目, dispatch_semaphore_wait 函数就相当于来了一辆车, dispatch_semaphore_signal
就相当于走了一辆车. 停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
调用一次 dispatch_semaphore_signal, 剩余的车位就增加一个; 调用一次 dispatch_semaphore_wait 剩余车位就减少一个;
当剩余车位为 0 时, 再来车 (即调用 dispatch_semaphore_wait) 就只能等待. 有可能同时有几辆车等待一个停车位. 有些车主
没有耐心, 给自己设定了一段等待时间, 这段时间内等不到停车位就走了, 如果等到了就开进去停车. 而有些车主就像把车停在这,
所以就一直等下去.
(7)代码举简单示例如下:
- dispatch_semaphore_t signal;
- signal = dispatch_semaphore_create(1);
- __block long x = 0;
- NSLog(@"0_x:%ld",x);
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- sleep(1);
- NSLog(@"waiting");
- x = dispatch_semaphore_signal(signal);
- NSLog(@"1_x:%ld",x);
- sleep(2);
- NSLog(@"waking");
- x = dispatch_semaphore_signal(signal);
- NSLog(@"2_x:%ld",x);
- });
- // dispatch_time_t duration = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000); // 超时 1 秒
- // dispatch_semaphore_wait(signal, duration);
- x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
- NSLog(@"3_x:%ld",x);
- x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
- NSLog(@"wait 2");
- NSLog(@"4_x:%ld",x);
- x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
- NSLog(@"wait 3");
- NSLog(@"5_x:%ld",x);
最终打印的结果为:
- 2018-12-02 22:51:54.734 LHTest[15700:70b] 0_x:0
- 2018-12-02 22:51:54.737 LHTest[15700:70b] 3_x:0
- 2018-12-02 22:51:55.738 LHTest[15700:f03] waiting
- 2018-12-02 22:51:55.739 LHTest[15700:70b] wait 2
- 2018-12-02 22:51:55.739 LHTest[15700:f03] 1_x:1
- 2018-12-02 22:51:55.739 LHTest[15700:70b] 4_x:0
- 2018-12-02 22:51:57.741 LHTest[15700:f03] waking
- 2018-12-02 22:51:57.742 LHTest[15700:f03] 2_x:1
- 2018-12-02 22:51:57.742 LHTest[15700:70b] wait 3
- 2018-12-02 22:51:57.742 LHTest[15700:70b] 5_x:0
NSConditionLock 和 NSCondition 条件锁
* 使用介绍:
NSConditionLock 好处是可以设置条件, 条件符合时获得锁. 设置时间, 指定时间之前获取锁. 缺点是加锁和解锁需要在同一线程中执行, 否则控制台会报错, 虽然不影响程序运行.(but 好像会影响进程释放, 因为多次执行后进程到了 80 多, 程序卡了还是崩溃了, 忘了. 只是猜测.)
* 使用举例:
- NSConditionLock * conditionLock = [[NSConditionLockalloc] init];
- // 当条件符合时获得锁
- [conditionLock lockWhenCondition:1];
- // 在指定时间前尝试获取锁, 若成功则返回 YES 否则返回 NO
- BOOL isLock = [conditionLock lockBeforeDate:date1];
- // 在指定时间前尝试获取锁, 且条件必须符合
- BOOL isLock = [conditionLock lockWhenCondition:1 beforeDate:date1];
- // 解锁并设置条件为 2
- [conditionLock unlockWithCondition:2];
来源: http://www.bubuko.com/infodetail-2869384.html