赞
踩
一,GCD简介
GCD是Apple开发的一个多线程的较新的解决方案。它主要用于优化应用程序以支持多核处理器以及其他对称处理系统。它是一个在线程池模式的基础上执行的并发任务。
为什么要使用GCD?
二,GCD任务和队列
sync
):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1 -- %@", [NSThread currentThread]); // 串行队列的创建方法 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3 -- %@",[NSThread currentThread]); }); NSLog(@"4 -- %@", [NSThread currentThread]); // Do any additional setup after loading the view. } 输出 2024-05-29 20:53:55.168850+0800 GCD详解[69722:2675459] 1 -- <_NSMainThread: 0x600000b70100>{number = 1, name = main} 2024-05-29 20:53:55.168911+0800 GCD详解[69722:2675459] 2 -- <_NSMainThread: 0x600000b70100>{number = 1, name = main} 2024-05-29 20:53:55.168958+0800 GCD详解[69722:2675459] 3 -- <_NSMainThread: 0x600000b70100>{number = 1, name = main} 2024-05-29 20:53:55.169010+0800 GCD详解[69722:2675459] 4 -- <_NSMainThread: 0x600000b70100>{number = 1, name = m
async
):异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1 -- %@", [NSThread currentThread]); // 串行队列的创建方法 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"3 -- %@",[NSThread currentThread]); }); NSLog(@"4 -- %@", [NSThread currentThread]); // Do any additional setup after loading the view. } 输出 2024-05-29 21:02:04.468604+0800 GCD详解[69868:2682284] 1 -- <_NSMainThread: 0x60000376c540>{number = 1, name = main} 2024-05-29 21:02:04.468673+0800 GCD详解[69868:2682284] 4 -- <_NSMainThread: 0x60000376c540>{number = 1, name = main} 2024-05-29 21:02:04.468678+0800 GCD详解[69868:2682427] 2 -- <NSThread: 0x600003765580>{number = 6, name = (null)} 2024-05-29 21:02:04.468735+0800 GCD详解[69868:2682427] 3 -- <NSThread: 0x600003765580>{number = 6, name = (null)}
Serial Dispatch Queue
):每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
Concurrent Dispatch Queue
):可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
⚠️注意:并发队列 的并发功能只有在异步(dispatch_async)方法下才有效。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1 -- %@", [NSThread currentThread]); // 并行队列的创建方法 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3 -- %@",[NSThread currentThread]); }); NSLog(@"4 -- %@", [NSThread currentThread]); // Do any additional setup after loading the view. } 输出 024-05-29 21:03:45.087904+0800 GCD详解[69913:2684176] 1 -- <_NSMainThread: 0x600001d0c400>{number = 1, name = main} 2024-05-29 21:03:45.087962+0800 GCD详解[69913:2684176] 2 -- <_NSMainThread: 0x600001d0c400>{number = 1, name = main} 2024-05-29 21:03:45.087992+0800 GCD详解[69913:2684176] 3 -- <_NSMainThread: 0x600001d0c400>{number = 1, name = main} 2024-05-29 21:03:45.088034+0800 GCD详解[69913:2684176] 4 -- <_NSMainThread: 0x600001d0c400>{number = 1, name = main}
GCD 的使用步骤只有两步:
1,创建一个队列(串行队列或并发队列);
2,将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)。
第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。队列的名称推荐使用应用程序 ID 这种逆序全程域名。
第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL
表示串行队列,DISPATCH_QUEUE_CONCURRENT
表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
1.所有放在主队列中的任务,都会放到主线程中执行。
2.可使用 dispatch_get_main_queue() 方法获得主队列。
⚠️注意:主队列其实并不特殊。 主队列的实质上就是一个普通的串行队列,只是因为默认情况下,平常写的代码是放在主队列中的,然后主队列中的代码,又都会放到主线程中去执行,所以才造成了主队列特殊的现象。
dispatch_queue_t queue = dispatch_get_main_queue();
可以使用 dispatch_get_global_queue 方法来获取全局并发队列。需要传入两个参数。第一个参数表示队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT
。第二个参数暂时没用,用 0 即可。
// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async。
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
虽然使用 GCD 只需两步,但是既然我们有两种队列(串行队列 / 并发队列),两种任务执行方式(同步执行 / 异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:
实际上,刚才还说了两种默认队列:全局并发队列、主队列。全局并发队列可以作为普通并发队列来使用。但是当前代码默认放在主队列中,所以主队列很有必要专门来研究一下,这样就有六种不同的组合方式了。
在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]);
输出结果
2024-05-29 21:30:27.212376+0800 GCD详解[70435:2704510] 1 -- <_NSMainThread: 0x6000022584c0>{number = 1, name = main}
2024-05-29 21:30:27.212433+0800 GCD详解[70435:2704510] 2 -- <_NSMainThread: 0x6000022584c0>{number = 1, name = main}
2024-05-29 21:30:27.212479+0800 GCD详解[70435:2704510] 3 -- <_NSMainThread: 0x6000022584c0>{number = 1, name = main}
2024-05-29 21:30:27.212530+0800 GCD详解[70435:2704510] 4 -- <_NSMainThread: 0x6000022584c0>{number = 1, name = main}
2024-05-29 21:30:27.212569+0800 GCD详解[70435:2704510] 5 -- <_NSMainThread: 0x6000022584c0>{number = 1, name = main}
从 同步执行 + 并发队列 中可看到:
NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]);
输出结果
024-05-29 21:36:49.626519+0800 GCD详解[70567:2709956] 1 -- <_NSMainThread: 0x600003d9c040>{number = 1, name = main}
2024-05-29 21:36:49.626591+0800 GCD详解[70567:2709956] 5 -- <_NSMainThread: 0x600003d9c040>{number = 1, name = main}
2024-05-29 21:36:51.631683+0800 GCD详解[70567:2710054] 2 -- <NSThread: 0x600003dd4c00>{number = 4, name = (null)}
2024-05-29 21:36:51.631683+0800 GCD详解[70567:2710050] 4 -- <NSThread: 0x600003dd4e00>{number = 5, name = (null)}
2024-05-29 21:36:51.631683+0800 GCD详解[70567:2710053] 3 -- <NSThread: 0x600003d9af40>{number = 3, name = (null)}
在 异步执行 + 并发队列 中可以看出:
NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]);
输出结果
2024-05-29 21:40:14.290797+0800 GCD详解[70741:2714482] 1 -- <_NSMainThread: 0x60000362c400>{number = 1, name = main}
2024-05-29 21:40:16.291817+0800 GCD详解[70741:2714482] 2 -- <_NSMainThread: 0x60000362c400>{number = 1, name = main}
2024-05-29 21:40:18.292859+0800 GCD详解[70741:2714482] 3 -- <_NSMainThread: 0x60000362c400>{number = 1, name = main}
2024-05-29 21:40:20.293898+0800 GCD详解[70741:2714482] 4 -- <_NSMainThread: 0x60000362c400>{number = 1, name = main}
2024-05-29 21:40:20.293968+0800 GCD详解[70741:2714482] 5 -- <_NSMainThread: 0x60000362c400>{number = 1, name = main}
在 同步执行 + 串行队列 可以看到:
NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]);
输出结果:
2024-05-29 21:43:18.017856+0800 GCD详解[70835:2717791] 1 -- <_NSMainThread: 0x6000037b8000>{number = 1, name = main}
2024-05-29 21:43:18.017922+0800 GCD详解[70835:2717791] 5 -- <_NSMainThread: 0x6000037b8000>{number = 1, name = main}
2024-05-29 21:43:20.023459+0800 GCD详解[70835:2717899] 2 -- <NSThread: 0x6000037f0540>{number = 5, name = (null)}
2024-05-29 21:43:22.028921+0800 GCD详解[70835:2717899] 3 -- <NSThread: 0x6000037f0540>{number = 5, name = (null)}
2024-05-29 21:43:24.033872+0800 GCD详解[70835:2717899] 4 -- <NSThread: 0x6000037f0540>{number = 5, name = (null)}
在 异步执行 + 串行队列 可以看到:
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]); }
这样会直接崩溃
这是因为我们在主线程中执行任务5方法,相当于把任务5放到了主线程的队列中。而 同步执行 会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把 任务2追加到主队列中,任务2就在等待主线程处理完5任务。而任务5需要等待任务1 所在队列的所有任务执行完毕,才能接着执行。这样相互等待,就造成了死锁。
- (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil]; } - (void)syncMain { NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]); }
输出结果:
2024-05-29 21:59:50.168185+0800 GCD详解[71147:2730839] 1 -- <NSThread: 0x6000027bc700>{number = 7, name = (null)}
2024-05-29 21:59:52.197328+0800 GCD详解[71147:2730626] 2 -- <_NSMainThread: 0x6000027d0000>{number = 1, name = main}
2024-05-29 21:59:54.203257+0800 GCD详解[71147:2730626] 3 -- <_NSMainThread: 0x6000027d0000>{number = 1, name = main}
2024-05-29 21:59:56.210364+0800 GCD详解[71147:2730626] 4 -- <_NSMainThread: 0x6000027d0000>{number = 1, name = main}
2024-05-29 21:59:56.210959+0800 GCD详解[71147:2730839] 5 -- <NSThread: 0x6000027bc700>{number = 7, name = (null)}
在其他线程中使用 同步执行 + 主队列 可看到:
这里任务2不用等待其他线程的任务5执行完,不会造成死锁。
NSLog(@"1 -- %@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"2 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"3 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"4 -- %@",[NSThread currentThread]); }); NSLog(@"5 -- %@", [NSThread currentThread]);
输出结果:
2024-05-30 19:47:21.114444+0800 GCD详解[96320:3468801] 1 -- <_NSMainThread: 0x6000011780c0>{number = 1, name = main}
2024-05-30 19:47:21.114507+0800 GCD详解[96320:3468801] 5 -- <_NSMainThread: 0x6000011780c0>{number = 1, name = main}
2024-05-30 19:47:23.140562+0800 GCD详解[96320:3468801] 2 -- <_NSMainThread: 0x6000011780c0>{number = 1, name = main}
2024-05-30 19:47:25.142281+0800 GCD详解[96320:3468801] 3 -- <_NSMainThread: 0x6000011780c0>{number = 1, name = main}
2024-05-30 19:47:27.143875+0800 GCD详解[96320:3468801] 4 -- <_NSMainThread: 0x6000011780c0>{number = 1, name = main}
在 异步执行 + 主队列 可以看到:
在 iOS 开发过程中,我们一般在主线程里边进行 UI 刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。
//获取全局并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获取主队列 dispatch_queue_t mainqueue = dispatch_get_main_queue(); NSLog(@"begin---%@", [NSThread currentThread]); dispatch_async(queue, ^{ //异步追加任务1 [NSThread sleepForTimeInterval:2]; NSLog(@"1---%@", [NSThread currentThread]); //回到主线程 dispatch_async(mainqueue, ^{ //追加到主线程中2执行任务 [NSThread sleepForTimeInterval:2]; NSLog(@"2---%@", [NSThread currentThread]); }); NSLog(@"3---%@", [NSThread currentThread]); }); NSLog(@"end---%@", [NSThread currentThread]);
输出结果
2024-05-30 20:15:39.553857+0800 GCD详解[96959:3489760] begin---<_NSMainThread: 0x600000a0c100>{number = 1, name = main}
2024-05-30 20:15:39.553924+0800 GCD详解[96959:3489760] end---<_NSMainThread: 0x600000a0c100>{number = 1, name = main}
2024-05-30 20:15:41.559224+0800 GCD详解[96959:3490016] 1---<NSThread: 0x600000a4dd40>{number = 8, name = (null)}
2024-05-30 20:15:41.559701+0800 GCD详解[96959:3490016] 3---<NSThread: 0x600000a4dd40>{number = 8, name = (null)}
2024-05-30 20:15:43.560499+0800 GCD详解[96959:3489760] 2---<_NSMainThread: 0x600000a0c100>{number = 1, name = main}
Dispatch Semaphore
(调度信号量)是 GCD(Grand Central Dispatch)提供的一种同步机制,用于控制并发访问资源或者在不同线程之间进行同步。信号量可以用来限制并发执行的任务数量,或者让一个线程等待某个事件的发生。
dispatch_semaphore_create 用于创建一个信号量,初始计数可以是任意非负整数。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 创建初始值为1的信号量
dispatch_semaphore_wait 用于等待信号量,如果信号量计数大于0,则减1并立即返回;否则阻塞当前线程,直到信号量计数大于0或者超时。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量,永远阻塞直到信号量计数大于0
dispatch_semaphore_signal 用于发送信号,即增加信号量的计数。如果有等待的线程,则唤醒一个等待的线程。
dispatch_semaphore_signal(semaphore); // 发送信号,增加信号量计数
假设你想限制最多只有2个任务同时执行,可以使用信号量来实现:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 最多允许2个任务同时执行
for (int i = 0; i < 5; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
NSLog(@"Task %d started", i);
sleep(2); // 模拟任务耗时2秒
NSLog(@"Task %d completed", i);
dispatch_semaphore_signal(semaphore); // 发送信号
});
}
NSLog(@"currentThread -- %@", [NSThread currentThread]); NSLog(@"semaphere --- begin"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphere = dispatch_semaphore_create(0); __block int number = 0; dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@"1 --- %@", [NSThread currentThread]); number = 100; dispatch_semaphore_signal(semaphere); }); dispatch_semaphore_wait(semaphere, DISPATCH_TIME_FOREVER); NSLog(@"semaphore --- end number = %d", number);
输出结果:
2024-05-30 21:27:19.082058+0800 GCD详解[98426:3541645] currentThread -- <_NSMainThread: 0x6000030f4000>{number = 1, name = main}
2024-05-30 21:27:19.082108+0800 GCD详解[98426:3541645] semaphere --- begin
2024-05-30 21:27:21.087427+0800 GCD详解[98426:3541817] 1 --- <NSThread: 0x6000030b2380>{number = 4, name = (null)}
2024-05-30 21:27:21.087923+0800 GCD详解[98426:3541645] semaphore --- end number = 100
semaphore—end 是在执行完 number = 100; 之后才打印的。而且输出结果 number 为 100。
执行顺如下:
异步执行任务转换为同步执行任务,可以对异步的执行结果进行进一步操作。
Dispatch Semaphore
在实际开发中主要用于:
先来看看不考虑线程安全的代码:
/** * 非线程安全:不使用 semaphore * 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票 */ NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"semaphore---begin"); self.ticketSurplusCount = 50; // queue1 代表北京火车票售卖窗口 dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL); // queue2 代表上海火车票售卖窗口 dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL); __weak typeof(self) weakSelf = self; dispatch_async(queue1, ^{ [weakSelf saleTicketNotSafe]; }); dispatch_async(queue2, ^{ [weakSelf saleTicketNotSafe]; }); /** * 售卖火车票(非线程安全) */ - (void)saleTicketNotSafe { while (1) { if (self.ticketSurplusCount > 0) { // 如果还有票,继续售卖 self.ticketSurplusCount--; NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]); [NSThread sleepForTimeInterval:0.2]; } else { // 如果已卖完,关闭售票窗口 NSLog(@"所有火车票均已售完"); break; } } }
/** * 线程安全:使用 semaphore 加锁 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票 */ - (void)initTicketStatusSave { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"semaphore---begin"); semaphoreLock = dispatch_semaphore_create(1); self.ticketSurplusCount = 50; // queue1 代表北京火车票售卖窗口 dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL); // queue2 代表上海火车票售卖窗口 dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL); __weak typeof(self) weakSelf = self; dispatch_async(queue1, ^{ [weakSelf saleTicketSafe]; }); dispatch_async(queue2, ^{ [weakSelf saleTicketSafe]; }); } /** * 售卖火车票(线程安全) */ - (void)saleTicketSafe { while (1) { // 相当于加锁 dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER); if (self.ticketSurplusCount > 0) { // 如果还有票,继续售卖 self.ticketSurplusCount--; NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]); [NSThread sleepForTimeInterval:0.2]; } else { // 如果已卖完,关闭售票窗口 NSLog(@"所有火车票均已售完"); // 相当于解锁 dispatch_semaphore_signal(semaphoreLock); break; } // 相当于解锁 dispatch_semaphore_signal(semaphoreLock); } }
运行结果
2024-05-30 21:54:54.019858+0800 GCD详解[99036:3563863] 剩余票数:10 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:54.222849+0800 GCD详解[99036:3563864] 剩余票数:9 窗口:<NSThread: 0x600003681bc0>{number = 3, name = (null)}
2024-05-30 21:54:54.428311+0800 GCD详解[99036:3563863] 剩余票数:8 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:54.632216+0800 GCD详解[99036:3563864] 剩余票数:7 窗口:<NSThread: 0x600003681bc0>{number = 3, name = (null)}
2024-05-30 21:54:54.833208+0800 GCD详解[99036:3563863] 剩余票数:6 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:55.037057+0800 GCD详解[99036:3563864] 剩余票数:5 窗口:<NSThread: 0x600003681bc0>{number = 3, name = (null)}
2024-05-30 21:54:55.237681+0800 GCD详解[99036:3563863] 剩余票数:4 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:55.439402+0800 GCD详解[99036:3563864] 剩余票数:3 窗口:<NSThread: 0x600003681bc0>{number = 3, name = (null)}
2024-05-30 21:54:55.643820+0800 GCD详解[99036:3563863] 剩余票数:2 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:55.849417+0800 GCD详解[99036:3563864] 剩余票数:1 窗口:<NSThread: 0x600003681bc0>{number = 3, name = (null)}
2024-05-30 21:54:56.055054+0800 GCD详解[99036:3563863] 剩余票数:0 窗口:<NSThread: 0x6000036c0dc0>{number = 5, name = (null)}
2024-05-30 21:54:56.260555+0800 GCD详解[99036:3563864] 所有火车票均已售完
2024-05-30 21:54:56.261010+0800 GCD详解[99036:3563863] 所有火车票均已售完
semaphoreLock = dispatch_semaphore_create(1)
;这里的信号量设置为1,就是为了保证只有一个线程同时运行售卖火车票的这段代码。保证了线程安全。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。