在IOS开发中,实现多线程的方式有多种: NSThread、NSOperation、GCD等
一、NSThread类实现多线程
#import "FZThread.h" @implementation FZThread +(void)initWithThread:(SubThread)subthread{ // 新建一个子线程:方法一 NSThread * t2 = [[NSThread alloc] initWithTarget:self selector:@selector(subThread) object:nil]; t2.name = @"t2"; [t2 start]; // 新建一个子线程:方法二 NSThread * t1 = [[NSThread alloc] initWithBlock:^{ subthread(); }]; // 设置子线程的名称 t1.name = @"t1"; // 线程开始执行 [t1 start]; } +(void)subThread{ // 子线程执行代码 NSLog(@"%@",[NSThread currentThread]); } @end
以上是使用 NSThread 类开始两个子线程的代码
NSThread 类的常用属性:
- name : 为子线程添加名字
- currentThread : 获取当前线程的信息
- mainThread : 获取主线程的相关信息
- threadPriority :子线程的优先级
- isMainThread : 判断当前是否是主线程
- finished : 判断线程是否执行完成
- cancelled : 判读线程是否取消
NSTread 类常用的方法:
- – (void)start; 线程开始的方法
- – (void)cancel; 取消线程的方法
- + (BOOL)isMultiThreaded; : 判断应用程序是否是多线程的
- + (void)sleepUntilDate:(NSDate *)date; 在指定时间之前将线程休眠
- + (void)sleepForTimeInterval:(NSTimeInterval)ti; 线程休眠指定时间
- + (void)exit; 手动结束线程
- + (double)threadPriority; 获取当前线程的优先级
- + (BOOL)setThreadPriority:(double)p; 设置当前线程的优先级
二、NSOperation
简介
由于NSOperation
该类是抽象类,因此您不直接使用它,而是使用子类或使用系统定义的子类之一(或)执行实际任务。我们可以选择自定义子类或许选择系统提供的子类: NSInvocationOperation、NSBlockOperation
示例代码:
NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{ // 子线程中执行的代码 NSLog(@"%@",[NSThread currentThread]); }]; // 开启线程 [operation start];
NSOperation 对象是单发对象,它只执行一次任务,结束后就不能再次执行它。所有我们通常把它队列(NSOperationQueue)一起配合使用。队列会自动执行队列中的线程,如果你不想使用队列,你可以调用start方法,手动开启线程。
示例代码:
+(void)initWithOperation{ // 线程1 NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; // 线程2 NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; // 线程队列 NSOperationQueue * queue = [[NSOperationQueue alloc] init]; [queue addOperation:operation1]; [queue addOperation:operation2]; }
操作依赖
什么是操作依赖呢?
操作依赖是一个线程的执行依赖另一个线程。
示例代码:
+(void)initWithOperation{ __block int num1,num2,sum; // 线程1 NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{ num1 = 10; }]; // 线程2 NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{ num2 = 20; }]; // 线程2 NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{ sum = num2 + num1; NSLog(@"%d",sum); }]; // 添加依赖 [operation3 addDependency:operation1]; [operation3 addDependency:operation2]; // 线程队列 NSOperationQueue * queue = [[NSOperationQueue alloc] init]; [queue addOperation:operation3]; [queue addOperation:operation1]; [queue addOperation:operation2]; }
添加依赖时,得出sum的值为 30 ,不添加依赖,sum的值不一定。
NSOperation 相关属性:
- isCancelled : 线程是否取消
- isAsynchronous : 线程是否是异步的
- isExecuting : 线程是否正在执行
- isFinished : 线程是否执行完毕
- isReady : 线程是否准备完毕
- dependencies : 获取线程的依赖
- queuePriority : 队列优先级
- completionBlock : 线程执行完成调用的Block
NSOperation 相关方法:
- -(void)start; 开始执行操作
- -(void)main; 执行接收者的非并行任务。
- -(void)cancel; 通知操作对象它应该停止执行其任务
- – (void) addDependency; 添加依赖
- – (void) removeDependency; 删除依赖
三、GCD
一、简介
GCD的实现C语言的API ,GCD中有任务和队列两个概念。
任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。
执行任务有两种方式:『同步执行』 和 『异步执行』。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
- 同步执行(sync): 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。只能在当前线程中执行任务,不具备开启新线程的能力。
- 异步执行(async): 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。可以在新的线程中执行任务,具备开启新线程的能力。
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。
在 GCD 中有两种队列:『串行队列』 和 『并发队列』。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue):
每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
二、创建队列
1、创建队列
创建队列使用dispatch_queue_create函数
第一个参数是队列名称,可以为空
第二个参数是队列类型:
DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
示例代码:
// 创建串行队列 dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); // 创建并行队列 dispatch_queue_t queue2 = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
对于串行队列,GCD还提供了主队列(Main Dispatch Queue),主队列中的所有任务都放在主线程中执行。
// 获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue();
对于并发队列,GCD默认提供了全局并发队列(Global Dispatch Queue)
可以使用 dispatch_get_global_queue 方法来获取全局并发队列。
传入两个参数:
第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT。
第二个参数暂时没用,用 0 即可。
// 获取全局并发队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
三、创建任务
GCD 提供了同步执行任务的创建方法dispatch_sync 和 异步执行创建任务方法 dispatch_sync
// 获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); // 创建同步任务 dispatch_sync(mainQueue, ^{ // 子线程中执行的代码 NSLog(@"%@",[NSThread currentThread]); }); // 获取全局并发队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 创建异步任务 dispatch_async(globalQueue, ^{ // 子线程中执行的代码 NSLog(@"%@",[NSThread currentThread]); });
四、任务和队列之间不同的组合
区别 | 并发队列 | 串行队列 | 主队列 |
同步(sync) | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行 | 死锁,卡住不执行 |
异步(async) | 有开启新线程,并发执行任务 | 有开启新线程(1条),串行执行任务 | 没有开启新线程,串行执行任务 |
五、GCD的基本使用
1、同步执行 + 并发队列
/** * 同步执行 + 并发队列 * 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。 */ - (void)syncConcurrent { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_sync(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_sync(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); NSLog(@"syncConcurrent---end"); }
2、异步执行 + 并发队列
/** * 异步执行 + 并发队列 * 特点:可以开启多个线程,任务交替(同时)执行。 */ - (void)asyncConcurrent { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"asyncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); NSLog(@"asyncConcurrent---end"); }
3、同步执行 + 串行队列
不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。
/** * 同步执行 + 串行队列 * 特点:不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。 */ - (void)syncSerial { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_sync(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_sync(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); NSLog(@"syncSerial---end"); }
4、异步执行 + 串行队列
/** * 异步执行 + 串行队列 * 特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。 */ - (void)asyncSerial { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"asyncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); NSLog(@"asyncSerial---end"); }
5、同步执行 + 主队列
同步执行 + 主队列 在不同线程中调用结果也是不一样,在主线程中调用会发生死锁问题,而在其他线程中调用则不会卡住,也不会开启新线程,执行完一个任务,再执行下一个任务。
6、异步执行 + 主队列
只在主线程中执行任务,执行完一个任务,再执行下一个任务
/** * 异步执行 + 主队列 * 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务 */ - (void)asyncMain { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"asyncMain---begin"); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); NSLog(@"asyncMain---end"); }
六、GCD的其他方法
1、GCD 栅栏方法:dispatch_barrier_async
我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于 栅栏 一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async 方法在两个操作组间形成栅栏。
dispatch_barrier_async 方法会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在 dispatch_barrier_async 方法追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行。
示例代码:
/** * 栅栏方法 dispatch_barrier_async */ - (void)barrier { dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ // 追加任务 1 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 2 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 }); // 栅栏 dispatch_barrier_async(queue, ^{ // 追加任务 barrier [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 3 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 }); dispatch_async(queue, ^{ // 追加任务 4 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 NSLog(@"4---%@",[NSThread currentThread]); // 打印当前线程 }); }
2、GCD 延时执行方法:dispatch_after
/** * 延时执行方法 dispatch_after */ - (void)after { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"asyncMain---begin"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2.0 秒后异步追加任务代码到主队列,并开始执行 NSLog(@"after---%@",[NSThread currentThread]); // 打印当前线程 }); }
3、GCD 一次性代码(只执行一次):dispatch_once
/** * 一次性代码(只执行一次)dispatch_once */ - (void)once { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 只执行 1 次的代码(这里面默认是线程安全的) }); }
4、GCD 快速迭代方法:dispatch_apply
使用不同的线程进行遍历
/** * 快速迭代方法 dispatch_apply */ - (void)apply { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"apply---begin"); dispatch_apply(6, queue, ^(size_t index) { NSLog(@"%zd---%@",index, [NSThread currentThread]); }); NSLog(@"apply---end"); }
七、GCD 队列组:dispatch_group
1、dispatch_group_notify
监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。
+(void)groupNotify{ // 首先会打印 NSLog(@"%@",[NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务1 [NSThread sleepForTimeInterval:2]; // 线程休眠两秒后打印 NSLog(@"%@",[NSThread currentThread]); }); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务2 [NSThread sleepForTimeInterval:4]; // 线程休眠4秒后打印 NSLog(@"%@",[NSThread currentThread]); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 前面的任务执行完毕之后,才会执行下面的任务 [NSThread sleepForTimeInterval:3]; NSLog(@"%@",[NSThread currentThread]); }); }
2、dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
+(void)groupNotify{ // 首先会打印 NSLog(@"%@",[NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务1 [NSThread sleepForTimeInterval:2]; // 线程休眠两秒后打印 NSLog(@"%@",[NSThread currentThread]); }); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务2 [NSThread sleepForTimeInterval:4]; // 线程休眠4秒后打印 NSLog(@"%@",[NSThread currentThread]); }); // 等待上面的任务执行完成之后才会继续往下执行 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 以下任务会等待任务1和任务2执行完成之后才会在执行 // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务3 [NSThread sleepForTimeInterval:2]; NSLog(@"%@",[NSThread currentThread]); }); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务4 [NSThread sleepForTimeInterval:2]; NSLog(@"%@",[NSThread currentThread]); }); }
3、dispatch_group_enter、dispatch_group_leave
dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1
dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。
当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。
+(void)groupNotify{ // 首先会打印 NSLog(@"%@",[NSThread currentThread]); dispatch_group_t group = dispatch_group_create(); // 执行任务数加一 dispatch_group_enter(group); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务1 [NSThread sleepForTimeInterval:2]; // 线程休眠两秒后打印 NSLog(@"%@",[NSThread currentThread]); // group 执行任务数减一 dispatch_group_leave(group); }); // 执行任务数加一 dispatch_group_enter(group); // 异步队列: 开启新线程 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 追加任务2 [NSThread sleepForTimeInterval:4]; // 线程休眠4秒后打印 NSLog(@"%@",[NSThread currentThread]); // 执行任务数减一 dispatch_group_leave(group); }); // 只有group任务数等于0的时候,下面的任务才会执行 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //回到主队列 NSLog(@"%@",[NSThread currentThread]); }); }
4、GCD 信号量:dispatch_semaphore
GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数小于 0 时等待,不可通过。计数为 0 或大于 0 时,计数减 1 且不等待,可通过。
Dispatch Semaphore 提供了三个方法:
1、dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
2、dispatch_semaphore_signal:发送一个信号,让信号总量加 1
3、dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
Dispatch Semaphore 在实际开发中主要用于:
1、保持线程同步,将异步执行任务转换为同步执行任务
2、保证线程安全,为线程加锁
4.1、 Dispatch Semaphore 实现线程同步
+(void)semaphoreSync{ NSLog(@"%@",[NSThread currentThread]); // 创建信号总量 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block int num = 0; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 线程阻塞2秒 [NSThread sleepForTimeInterval:2]; num = 100; // 发送一个信号,让信号总量加一 dispatch_semaphore_signal(semaphore); }); // 可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"%d",num); }
Dispatch Semaphore 执行步骤:
1、semaphore 初始创建时计数为 0。
2、异步执行 将 任务 1 追加到队列之后,不做等待,接着执行 dispatch_semaphore_wait 方法,semaphore 减 1,此时 semaphore == -1,当前线程进入等待状态。
3、然后,异步任务 1 开始执行。任务 1 执行到 dispatch_semaphore_signal 之后,总信号量加 1,此时 semaphore == 0,正在被阻塞的线程(主线程)恢复继续执行。
4、最后打印 semaphore—end,number = 100。
4.2、Dispatch Semaphore 线程安全和线程同步(为线程加锁)
多个线程访问同一个变量,并执行写的操作,那么就需要对考虑线程安全,需要对写操作加锁。
-(void)writeSum{ _sum = 50; // 创建信息总量 _semaphore = dispatch_semaphore_create(1); dispatch_queue_t queue1 = dispatch_queue_create("num1", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue2 = dispatch_queue_create("num2", DISPATCH_QUEUE_SERIAL); // 异步线程1 dispatch_async(queue1, ^{ [self numChange]; }); // 异步线程2 dispatch_async(queue2, ^{ [self numChange]; }); } #pragma mark- 修改sum的数量 -(void)numChange{ while (1) { // 为线程加锁 dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER); if (_sum >0) { self.sum --; NSLog(@"Thred:%@----sum:%d",[NSThread currentThread],_sum); }else{ NSLog(@"Thred:%@----sum:%d",[NSThread currentThread],_sum); // 解锁 dispatch_semaphore_signal(_semaphore); break; } // 解锁 dispatch_semaphore_signal(_semaphore); } }
程序的打印结果是按照顺序打印的,没有出现混乱的现象。