iOS基础 - GCD:线程中的代码执行顺序 | 死锁
▶ 线程死锁
死锁:是指两个或两个以上的进程(线程)在执行过程中,因争夺资源(如数据源,内存等。注:变量不是资源)而造成的一种互相等待的现象!若无外部处理作用,它们都将无限等待
死锁形成的原因有
A. 系统资源不足
B. 进程(线程)推迸的順序不恰当
C. 资源分配不当
死锁形成的条件有
A. 互斥条件:所谓互斥就是进程在某一时间内独占资源
B. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
C. 不剥夺条件:进程已获得资源,在木使用完之前,不能强行剥夺
D. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
▶ 代码示例
测试一:请尝试说出以下代码的执行顺序
1 #import "ViewController.h" 2 // 并行队列 3 #define concurrentQueueA dispatch_queue_create("concurrentQueueA", DISPATCH_QUEUE_CONCURRENT) 4 #define concurrentQueueB dispatch_queue_create("concurrentQueueB", DISPATCH_QUEUE_CONCURRENT) 5 // 串行队列 6 #define serialQueueA dispatch_queue_create("concurrentQueueA", DISPATCH_QUEUE_SERIAL) 7 #define serialQueueB dispatch_queue_create("concurrentQueueB", DISPATCH_QUEUE_SERIAL) 8 // 全局队列 9 #define globalQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 10 // 主队列 11 #define mainQueue dispatch_get_main_queue() 12 13 @implementation ViewController 14 15 - (void)viewDidLoad { 16 [super viewDidLoad]; 17 18 [self DemoOne]; 19 [self DemoTwo]; 20 [self DemoThree]; 21 [self DemoFour]; 22 [self DemoFive]; 23 [self DemoSix]; 24 [self DemoSeven]; 25 } 26 27 //--------------------- 测试1 --------------------- 28 - (void)DemoOne{ 29 30 NSLog(@"任务1"); 31 32 dispatch_async(concurrentQueueA,^{ 33 NSLog(@"任务2"); 34 35 dispatch_async(concurrentQueueA,^{ 36 NSLog(@"任务3"); 37 }); 38 39 NSLog(@"任务4"); 40 }); 41 42 NSLog(@"任务5"); 43 } 44 // 执行结果 1 5 2 4 3 45 46 //--------------------- 测试2 --------------------- 47 - (void)DemoTwo{ 48 49 NSLog(@"任务1"); 50 51 // 任务2是异步,不会阻塞线程,继续向下执行,先打印 任务3 52 dispatch_async(serialQueueA, ^{ 53 NSLog(@"任务2"); 54 }); 55 56 NSLog(@"任务3"); 57 58 // 因为任务4 和 任务2 在同一串行队列 59 // 根据队列先进先出原则,任务4 必须等 任务2 执行后才能执行 60 // 任务4 是同步任务,会阻塞线程 61 // 只有执行完 任务4 才能继续向下执行打印5 62 dispatch_sync(serialQueueA, ^{ 63 NSLog(@"任务4"); 64 }); 65 NSLog(@"任务5"); 66 67 } 68 // 执行结果 1 3 2 4 5 69 70 //--------------------- 测试3 --------------------- 71 - (void)DemoThree{ 72 73 NSLog(@"任务1"); 74 75 // 任务2 是异步,则先打印 任务3 76 dispatch_async(serialQueueA, ^{ 77 78 // 队列于队列之间是并发的关系 79 // 就是说 任务2 和 任务4 是并发的 80 81 // // 可使用耗时操作可验证 4 5 2 的顺序 82 // for (int k = 0; k < 5000; k++) { 83 // NSLog(@"任务2的k值===%d",k); 84 // } 85 86 NSLog(@"任务2"); 87 }); 88 89 NSLog(@"任务3"); 90 91 // 任务4 是同步,任务5要等待 任务4 执行完毕后才能执行 92 // 任务5 必须在 任务4 后才能执行 93 dispatch_sync(serialQueueB, ^{ 94 [NSThread sleepForTimeInterval:1.0]; 95 NSLog(@"任务4"); 96 }); 97 NSLog(@"任务5"); 98 } 99 // 执行结果 1 3 2 4 5 100 // 实际存在多种情况 1 3 245 或 425 或 452 101 102 //--------------------- 测试4 --------------------- 103 - (void)DemoFour{ 104 105 NSLog(@"任务1"); 106 107 // dispatch_sync 阻塞线程:必须等待block返回,才能执行task3 108 // 而当前主队列中正在被 任务1 执行,必须等待完成 任务3 完成后才能释放 109 // 这就造成了 任务3 等待 block 完成返回 110 // block 缺又要等待 任务3 的完成去释放主队列,进造成了相互等待的循环 111 dispatch_sync(mainQueue, ^{ 112 NSLog(@"任务2"); 113 }); 114 115 NSLog(@"任务3"); 116 } 117 // 执行结果:crash 118 119 //--------------------- 测试5 --------------------- 120 - (void)DemoFive{ 121 122 NSLog(@"任务1"); 123 124 dispatch_sync(serialQueueA, ^{ 125 NSLog(@"任务2"); 126 }); 127 128 NSLog(@"任务3"); 129 } 130 // 执行结果:1 2 3 131 132 //--------------------- 测试6 --------------------- 133 - (void)DemoSix{ 134 135 NSLog(@"任务1"); 136 137 // 串行队列中追加的 任务3 和 串行队列中原有的 任务4 两者之间相互等待 138 dispatch_async(serialQueueA, ^{ 139 140 NSLog(@"任务2"); 141 142 dispatch_sync(serialQueueA, ^{ 143 NSLog(@"任务3"); 144 }); 145 146 // // 将 任务3 放置进区别于 serialQueueA 的队列,不会 crash 147 // // 这样就阻塞了当前队列 serialQueueA,依次顺序执行 148 // // 执行结果 1 5 2 3 4 149 // dispatch_sync(serialQueueB, ^{ 150 // NSLog(@"任务3"); 151 // }); 152 153 NSLog(@"任务4"); 154 }); 155 156 NSLog(@"任务5"); 157 158 // 任务2、任务4 和 任务3 在同一队列中执行,dispatch_sync 确定了 任务4 需要等待 任务3 完成后返回才能执行 159 // 而 任务2 执行的时候已经占用了 当前队列serialQueueA,需要等到 任务4 完成后才能释放该队列 160 // 这就造成了 任务3 等待 任务4 的完成;而 任务4 又要等待 任务3 的返回结果才能执行 161 162 } 163 // 执行结果:crash 164 165 //--------------------- 测试7 --------------------- 166 - (void)DemoSeven{ 167 168 // 异步 169 dispatch_async(concurrentQueueA, ^{ 170 NSLog(@"任务1"); 171 }); 172 // 异步 173 dispatch_async(concurrentQueueA, ^{ 174 NSLog(@"任务2"); 175 }); 176 // 同步 177 dispatch_sync(concurrentQueueA, ^{ 178 NSLog(@"任务3"); 179 }); 180 181 // 主线程 182 // ------------------ 183 NSLog(@"任务0"); 184 // ------------------ 185 186 187 // 异步 188 dispatch_async(concurrentQueueA, ^{ 189 NSLog(@"任务4"); 190 }); 191 // 异步 192 dispatch_async(concurrentQueueA, ^{ 193 NSLog(@"任务5"); 194 }); 195 // 同步 196 dispatch_sync(concurrentQueueA, ^{ 197 NSLog(@"任务6"); 198 }); 199 } 200 201 // 执行结果: 1 2 3 无序执行; 0 必定在中间 第4位 执行; 4 5 6 无序执行 202 203 @end
测试二:多线程是不安全的,使用不规范极易出现抢占公共资源的问题
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 __block int a = 0; 5 while (a < 4) { 6 7 // 异步执行:会出现抢占资源问题 8 // 极有可能在一瞬间存在多个子线程同时在执行 a++ 9 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 10 NSLog(@"当前线程:%@",[NSThread currentThread]); 11 a++; 12 }); 13 } 14 15 // 输出结果基本上可以说必定是 >= 4 16 NSLog(@"验证猜想:%d",a); 17 }