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 }

 

posted on 2022-10-20 01:55  低头捡石頭  阅读(20)  评论(0编辑  收藏  举报

导航