有固定ip自己做网站/站点查询
一、多线程基础知识
1.1 什么是进程?
进程是指在系统中正在运行的一个应用程序。对于电脑而已,你打开一个软件,就相当于开启了一个进程。对于手机而已,你打开了一个APP,就相当于开启了一个进程。
1.2 什么是线程?
线程是进程的基本执行单位。一个进程中至少会有一条线程,当然也可能会有多条线程。比如你使用QQ音乐听歌,系统会创建一条线程去播放音乐。使用QQ音乐下载歌曲,系统会创建一条线程去下载歌曲。这两个操作是可以同时进行的,也就说一个进程中可以同时运行多条线程。
1.3 任务执行的方式
任务执行的方式分为两种:串行和并行,也可以叫做同步和异步。串行指多个任务一个一个排队的执行,并行指多个任务并列执行。
1.4 多线程是什么?
多线程就是一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
1.5多线程可以干什么?
多线程可以同时执行不同的任务,提高系统的运行效率。
1.6 多线程的优点
(1) 多线程可以同时执行不同的任务,提高系统的运行效率。
(2) 在app开发中使用多线程,可以减少页面卡顿,提升流畅度。
1.7 多线程的缺点
(1) 增大CPU调度开销:线程越多,CPU在调度线程上的开销就越大。
(2) 增加程序的复杂性:比如线程之间的通信、多线程的数据共享。
1.8 iOS有四种多线程编程技术,分别是:
(1) pThread
(2) NSThread
(3) Cocoa NSOperation
(4) CGD (全称:Grand Central Dispathch)
这几种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
二、GCD
2.1 GCD简介
在 iOS 开发中,GCD(Grand Central Dispatch)是一种用于实现并发编程的技术,它提供了一套强大的 API,帮助开发者管理并发任务、线程池、任务调度等。GCD 的主要特点和优势包括:
-
简单易用:GCD 提供了一套简单而强大的 API,可以帮助开发者轻松实现多线程编程和异步操作,而不需要手动管理线程的创建和销毁。
-
高效性能:GCD 可以利用系统资源高效地管理并发任务,根据系统负载和任务优先级动态调度任务的执行,提高应用程序的性能和响应速度。
-
多线程支持:GCD 支持串行队列、并发队列、主队列等不同类型的队列,可以满足不同场景下的多线程需求,实现任务的并发执行和按顺序执行。
-
线程安全:GCD 自动处理了线程同步和锁的问题,可以避免多线程编程中常见的竞争条件和数据访问冲突,提高程序的稳定性。
-
异步操作:GCD 支持异步操作,可以在后台执行耗时的任务,避免阻塞主线程,保持应用的流畅性和响应性。
在 iOS 开发中,开发者可以使用 GCD 来实现各种并发任务,例如网络请求、数据处理、图片加载等。常用的 GCD API 包括:
dispatch_async
:将任务提交到队列中异步执行。dispatch_sync
:将任务提交到队列中同步执行。dispatch_group
:用于管理一组任务的执行。dispatch_barrier_async
:在并发队列中插入一个 barrier,保证前面的任务在 barrier 之前执行,后面的任务在 barrier 之后执行。
总的来说,GCD 是 iOS 开发中非常重要的并发编程工具,可以帮助开发者实现多线程编程、异步操作和任务调度,提高应用程序的性能和用户体验。熟练掌握 GCD 可以让开发者更好地处理并发编程中的各种问题,提高代码的质量和可维护性。
GCD是苹果提出的异步执行任务的技术,用简单的方法实现了多线程编程。GCD用的也是C语言,里面引入了OC的block语法,使用起来更加的灵活和方便。
二、GCD的基本使用
2.1 如何创建一个线程
CGD创建一个线程有两种方式,一种是利用全局的dispatch_get_global_queue
来创建,另外一种是自定义创建线程,即可以自己给线程取名字的方式来创建线程。
2.1.1 利用全局的dispatch_get_global_queue
来创建线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"start task 1");//执行耗时任务[NSThread sleepForTimeInterval:3];dispatch_async(dispatch_get_main_queue(), ^{//回到主线程刷新UINSLog(@"回到主线程刷新UI");});});
2.1.2 自定义创建线程
这种推荐使用应用程序ID这种逆序全程域名,如果嫌命名麻烦设置为NULL也可以。给Dispatch Queue署名,是为了在程序崩溃的时候,能够更快速的找到出错的线程。
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", NULL);dispatch_async(queue, ^{NSLog(@"start task 1");[NSThread sleepForTimeInterval:3];NSLog(@"end task 1");
});
2.2 线程的设置
创建了一个线程之后,可以对线程进行设置。可以设置的内容包括:设置线程的执行方式和线程的执行顺序。
dispatch_get_global_queue(0, 0)
有两个参数,第一个参数是设置线程的优先级,第二个参数是设置线程的执行方式。
2.2.1 设置线程执行方式
线程的执行方式有两种:串行和并行,即同步和异步。
Dispatch Queue的种类:Serial Dispatch Queue(串行) 和 Concurrent Dispatch Queue(并行)。
//线程执行方式:串行,也可填0,0就代表Serial Dispatch Queue
dispatch_async(dispatch_get_global_queue(0, Serial Dispatch Queue), ^{NSLog(@"start task 1");//执行耗时任务[NSThread sleepForTimeInterval:3];
});
//线程执行方式:串行,也可填NULL,NULL就代表Serial Dispatch Queue
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", Serial Dispatch Queue);dispatch_async(queue, ^{NSLog(@"start task 1");[NSThread sleepForTimeInterval:3];NSLog(@"end task 1");
});
2.2.2 设置线程执行顺序
线程的执行顺序有以下几种:
- 默认:
DISPATCH_QUEUE_PRIORITY_DEFAULT
- 高:
DISPATCH_QUEUE_PRIORITY_HIGH
- 低:
DISPATCH_QUEUE_PRIORITY_LOW
- 后台:
DISPATCH_QUEUE_PRIORITY_BACKGROUND
//在默认优先级的 Global Dispatch Queue 中执行Block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{//可并行执行的处理//在Main Dispatch Queue中执行Blockdispatch_async(dispatch_get_main_queue(), ^{//只能在主线程中执行的处理});
});
对于自定义创建的线程,需要用dispatch_set_target_queue
设置线程优先级:
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_set_target_queue(queue, DISPATCH_QUEUE_PRIORITY_HIGH);
三、GCD队列与函数
3.1 GCD 串行队列与并发队列
在 GCD(Grand Central Dispatch)中,队列是用来管理任务的执行顺序的一种机制。GCD 中的队列分为两种类型:串行队列(Serial Queue)和并发队列(Concurrent Queue)。
-
串行队列(Serial Queue):
串行队列中的任务按照先进先出(FIFO)的顺序依次执行,每次只执行一个任务。一个任务执行完成后,才会执行下一个任务。串行队列可以确保任务按照特定的顺序执行,适用于需要顺序执行任务的场景。 -
并发队列(Concurrent Queue):
并发队列中的任务可以同时执行,可以并发地执行多个任务。并发队列中的任务的执行顺序是不确定的,可能同时执行多个任务,也可能顺序执行。并发队列适用于需要同时执行多个任务且任务之间相互独立的场景。
在 GCD 中,有以下几种类型的队列:
- 主队列(Main Queue):串行队列,用于在主线程上执行任务,通常用于更新 UI。
- 全局并发队列(Global Concurrent Queue):系统提供的并发队列,有多个优先级(QoS)可供选择。
- 自定义队列(Custom Queue):通过
dispatch_queue_create
函数创建的自定义队列,可以设置为串行队列或并发队列。
//主队列,相当于主线程的queue,有且仅有一个。
dispatch_get_main_queue// 全局并发队列,相当于全局的queue,可以有多个。
dispatch_get_global_queue//自定义队列, 普通串行队列
dispatch_queue_t serial = dispatch_queue_create("com.test.Serial", DISPATCH_QUEUE_SERIAL); 普通串行队列//自定义队列,普通并发队列
dispatch_queue_t concurrent = dispatch_queue_create("comd.test.concurrent", DISPATCH_QUEUE_CONCURRENT);
3.2 GCD 同步函数与异步函数
在 GCD(Grand Central Dispatch)中,dispatch_async
和 dispatch_sync
是两个常用的函数,用于将任务提交到队列中异步或同步执行。
3.2.1 dispatch_async
dispatch_async
函数用于异步地将任务提交到指定的队列中执行。该函数会立即返回,不会等待任务执行完成,而是在后台进行执行。适用于不需要等待任务执行完成就可以继续执行后续代码的场景。
使用方法如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{// 任务代码块NSLog(@"Task executed asynchronously");
});
3.2.2 dispatch_sync
dispatch_sync
函数用于同步地将任务提交到指定的队列中执行。该函数会阻塞当前线程,直到任务执行完成才会继续执行后续代码。适用于需要等待任务执行完成后再继续执行后续代码的场景。
使用方法如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{// 任务代码块NSLog(@"Task executed synchronously");
});
区别和用法总结:
dispatch_async
是异步执行任务,不会阻塞当前线程,适用于并发执行任务的场景。dispatch_sync
是同步执行任务,会阻塞当前线程,等待任务执行完成后才会继续执行后续代码。- 通常情况下,推荐使用
dispatch_async
来避免阻塞主线程,提高程序的响应速度。 - 在需要确保任务按照特定顺序执行或需要等待任务执行完成后再继续执行后续代码的情况下,可以使用
dispatch_sync
。
总之,dispatch_async
和 dispatch_sync
是 GCD 中用于提交任务到队列中异步或同步执行的两个重要函数,根据具体的需求选择合适的函数来提交任务,可以更好地控制任务的执行方式。
四、函数与队列的组合
在上一小节中,我们知道GCD中函数有两种 同步和异步,队列分为两种 串行和并发,加上两种特殊的队列 主队列和全局并发队列,所以可以有如下几种组合:
- 同步函数 + 主队列
- 同步函数 + 全局并发队列
- 同步函数 + 普通串行队列
- 同步函数 + 普通并发队列
- 异步函数 + 主队列
- 异步函数 + 全局并发队列
- 异步函数 + 普通串行队列
- 异步函数 + 普通并发队列
其实主队列是一种特殊的串行队列,全局并发队列是一种特殊的并发队列,不过在某些情况下与普通队列有所不同,我们分别通过demo来看下这几种组合会有什么效果。
4.1 同步函数 + 主队列
同步函数 + 主队列的代码如下:
- (void)syncMain {dispatch_queue_t mainQueue = dispatch_get_main_queue();NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_sync(mainQueue, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);});NSLog(@"3 ---- %@",[NSThread currentThread]);
}
代码执行结果如下:
可以看到同步函数 + 主队列的执行结果是发生了死锁,_dispatch_sync_f_slow () 是发生死锁时GCD调用的函数。发生死锁的原因如下:
4.2 异步函数 + 主队列
异步函数 + 主队列的代码如下:
- (void)asyncMain {dispatch_queue_t mainQueue = dispatch_get_main_queue();NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_async(mainQueue, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);});NSLog(@"3 ---- %@",[NSThread currentThread]);
}
代码执行结果如下:
从执行结果可以发现,使用异步函数没有阻塞后面的任务,因此也不会发生死锁。并且可以发现,在主队列下使用异步函数也不会开启新的线程。
4.3 同步函数 + 全局并发队列
同步函数 + 全局并发队列的代码如下:
- (void)syncGlobal {dispatch_queue_t global = dispatch_get_global_queue(0, 0);NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_sync(global, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);dispatch_sync(global, ^{NSLog(@"3 ---- %@",[NSThread currentThread]);});NSLog(@"4 ---- %@",[NSThread currentThread]);});NSLog(@"5 ---- %@",[NSThread currentThread]);
}
这次代码稍微复杂一些,内部再嵌套了一个sync函数,其执行结果如下:
通过结果可以发现,在全局并发队列下,使用sync不会死锁。并且同步函数不会开启新的线程,因此虽然是在全局并发队列中,但是依然是在主线程。
4.4 异步函数 + 全局并发队列
同步函数 + 全局并发队列的代码如下:
- (void)syncGlobal {dispatch_queue_t global = dispatch_get_global_queue(0, 0);NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_async(global, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);dispatch_async(global, ^{NSLog(@"3 ---- %@",[NSThread currentThread]);});NSLog(@"4 ---- %@",[NSThread currentThread]);});NSLog(@"5 ---- %@",[NSThread currentThread]);
}
代码执行结果如下:
可以发现代码是异步执行,不会阻塞,也不会死锁,并且async函数会开启新的线程6和7。
4.5 同步函数 + 普通串行队列
同步函数 + 普通串行队列的代码,我们分两部分看,先看第一部分如下:
- (void)syncSerial {dispatch_queue_t serial = dispatch_queue_create("BPTest.sync.Serial", DISPATCH_QUEUE_SERIAL);NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_sync(serial, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);
// dispatch_sync(serial, ^{
// NSLog(@"3 ---- %@",[NSThread currentThread]);
// });
// NSLog(@"4 ---- %@",[NSThread currentThread]);});NSLog(@"3 ---- %@",[NSThread currentThread]);
}
执行结果如下:
可以发现此处并未发生死锁,对比同步函数 + 主队列,同样都是串行队列,为何主队列会死锁闪退,而普通的串行队列可以正常运行呢?其原因如下图:
同步函数 + 主队列中,因为只有一个主队列,所以会发生死锁,而在同步函数 + 串行队列中,因为有除了有一个串行队列外,还有个默认的主队列,我们的次任务是添加到串行队列中的,因此不会死锁闪退。
那么同步函数 + 串行队列一定是安全的吗?我们接着看下面的代码:
可以发现,同步函数 + 串行队列一样会发生闪退,那么我们分析下这次死锁闪退的原因,如下图所示:
其实原因与主队列闪退是一致的,本次所添加的任务都是添加到我们创建的串行队列中,所以会发生和主队列一样死锁闪退。
4.6 异步函数 + 普通串行队列
异步函数 + 普通串行队列的代码如下:
- (void)asyncSerial {dispatch_queue_t serial = dispatch_queue_create("BPTest.async.Serial", DISPATCH_QUEUE_SERIAL);NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_async(serial, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);dispatch_async(serial, ^{NSLog(@"3 ---- %@",[NSThread currentThread]);});NSLog(@"4 ---- %@",[NSThread currentThread]);});NSLog(@"5 ---- %@",[NSThread currentThread]);
}
代码执行结果如下:
虽然还是添加在串行队列中,但是因为使用的是异步函数,不会发生阻塞,所以也不会产生死锁。
4.7 同步函数 + 普通并发队列
同步函数 + 普通并发队列的代码如下:
- (void)syncConcurrent {dispatch_queue_t concurrent = dispatch_queue_create("BPTest.sync.concurrent", DISPATCH_QUEUE_CONCURRENT);NSLog(@"1 ---- %@",[NSThread currentThread]);dispatch_sync(concurrent, ^{NSLog(@"2 ---- %@",[NSThread currentThread]);dispatch_sync(concurrent, ^{NSLog(@"3 ---- %@",[NSThread currentThread]);});NSLog(@"4 ---- %@",[NSThread currentThread]);});NSLog(@"5 ---- %@",[NSThread currentThread]);
}
执行结果如下:
与同步函数 + 全局并发队列的情况一致,也不会发生死锁闪退,这里分析下为何使用并发队列没有闪退,而用串行队列闪退了,分析如图:
4.8 异步函数 + 普通并发队列
代码及执行结果如下:
这里既不会阻塞,也不会死锁。
4.9 总结
本篇主要介绍了GCD的队列和函数,可以得到以下几个结论:
- 函数分为同步函数和异步函数,函数控制是否有开辟线程的能力,同步函数不具有开启新线程的能力,异步函数具有开辟新线程的能力,但是会根据实际情况确定是否开辟新线程
- 队列主要分为串行队列和并发队列,队列决定了线程的调度能力,串行队列只能调度一个线程,因此任务只能顺序执行,并发队列则具有并发调度的能力。
队列和函数的组合有以下几个结果:
队列 | 同步 | 异步 |
---|---|---|
主队列 | 死锁闪退 | 正常,但不会开辟新线程(主队列中只有主线程) |
全局并发队列 | 正常,同步不开辟新线程 | 正常,开辟新线程 |
普通串行队列 | 部分情况下会死锁闪退 | 正常,会开辟新线程 |
普通并发列 | 正常,同步函数不开启新线程 | 正常,会开辟新线程 |
五、GCD线程组
在 GCD(Grand Central Dispatch)中,线程组(Dispatch Group)是一种用于管理多个任务的机制,它可以帮助我们在多个任务执行完成后执行特定的代码块。线程组可以让我们将一组任务关联在一起,然后等待这些任务全部完成后再执行其他操作。
下面是线程组的一些重要概念和使用方法:
-
创建线程组:
- 使用
dispatch_group_create
函数来创建一个线程组对象。
- 使用
-
将任务添加到线程组中:
- 使用
dispatch_group_async
函数将任务异步提交到指定的队列中,并将该任务与线程组关联起来。
- 使用
-
等待线程组中的任务执行完成:
- 使用
dispatch_group_wait
函数可以等待线程组中的所有任务执行完成,该函数会阻塞当前线程直到所有任务执行完成。 - 使用
dispatch_group_notify
函数可以在线程组中的所有任务执行完成后执行特定的代码块,不会阻塞当前线程。
- 使用
5.1 dispatch_group_wait
函数
在 Objective-C 中,GCD(Grand Central Dispatch)提供了 dispatch_group_wait
函数,用于等待指定的 dispatch group 中的所有任务执行完成。dispatch_group_wait
函数会阻塞当前线程,直到指定的 dispatch group 中的所有任务都执行完成或超时。
使用 dispatch_group_wait
函数的步骤如下:
- 创建一个 dispatch group,并将需要等待的任务添加到该 group 中。
- 使用
dispatch_group_wait
函数等待指定的 dispatch group 中的任务执行完成。 - 任务执行完成后,继续执行后续代码。
下面是一个示例代码,演示了如何使用 dispatch_group_wait
函数等待指定的 dispatch group 中的任务执行完成:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 添加任务到 dispatch group
dispatch_group_async(group, queue, ^{// 任务1NSLog(@"Task 1 executed");
});dispatch_group_async(group, queue, ^{// 任务2NSLog(@"Task 2 executed");
});// 等待 dispatch group 中的任务执行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);// 所有任务执行完成后,继续执行后续代码
NSLog(@"All tasks executed");
在上面的示例中,首先创建了一个 dispatch group,并向该 group 中添加了两个任务。然后使用 dispatch_group_wait
函数等待 dispatch group 中的任务执行完成。一旦所有任务执行完成,dispatch_group_wait
函数返回,继续执行后续代码。
需要注意的是,dispatch_group_wait
函数会阻塞当前线程,直到所有任务执行完成或超时。因此,在使用该函数时,需要确保不会导致死锁或线程阻塞的情况发生。
总之,dispatch_group_wait
函数是 GCD 中用于等待指定的 dispatch group 中的任务执行完成的函数,可以帮助控制任务的执行顺序和同步。
5.2 dispatch_group_notify
函数
在 Objective-C 中,GCD(Grand Central Dispatch)提供了 dispatch_group_notify
函数,用于在指定的 dispatch group 中的所有任务执行完成后执行一个回调块。这个函数通常与 dispatch_group_enter
和 dispatch_group_leave
配合使用,用于异步执行一组任务并在所有任务执行完成后执行额外的操作。
以下是 dispatch_group_notify
函数的介绍和使用方法:
dispatch_group_notify
:
dispatch_group_notify
函数用于设置一个回调块,在指定的 dispatch group 中的所有任务执行完成后执行。当 dispatch group 中的任务计数为零时,即所有任务执行完成时,指定的回调块会被异步执行。
使用 dispatch_group_notify
函数的步骤如下:
- 创建一个 dispatch group。
- 在需要执行的任务开始前调用
dispatch_group_enter
。 - 在任务执行完成后调用
dispatch_group_leave
。 - 使用
dispatch_group_notify
函数设置一个回调块,在所有任务执行完成后执行额外的操作。
下面是一个示例代码,演示了如何使用 dispatch_group_notify
函数在所有任务执行完成后执行额外的操作:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_enter(group);
dispatch_async(queue, ^{// 任务1NSLog(@"Task 1 executed");dispatch_group_leave(group);
});dispatch_group_enter(group);
dispatch_async(queue, ^{// 任务2NSLog(@"Task 2 executed");dispatch_group_leave(group);
});// 设置一个回调块,在所有任务执行完成后执行额外的操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"All tasks executed");
});// 等待所有任务执行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
在上面的示例中,首先创建了一个 dispatch group,并向该 group 中添加了两个任务。在每个任务开始前调用 dispatch_group_enter
,在任务执行完成后调用 dispatch_group_leave
。最后使用 dispatch_group_notify
函数设置一个回调块,在所有任务执行完成后打印信息。
通过使用 dispatch_group_notify
函数,可以在所有任务执行完成后执行额外的操作,实现对一组任务的控制和同步。
5.3 dispatch_group_enter
和dispatch_group_leave
函数
在 Objective-C 中,GCD(Grand Central Dispatch)提供了 dispatch_group_enter
和 dispatch_group_leave
函数,用于管理 dispatch group 中的任务计数。这两个函数通常与 dispatch_group_notify
配合使用,用于异步执行一组任务并在所有任务执行完成后执行额外的操作。
以下是 dispatch_group_enter
和 dispatch_group_leave
函数的介绍和使用方法:
-
dispatch_group_enter
:
dispatch_group_enter
函数用于向指定的 dispatch group 中添加一个任务,增加该 group 的任务计数。在任务开始执行前调用dispatch_group_enter
,表示有一个任务要执行。 -
dispatch_group_leave
:
dispatch_group_leave
函数用于标记指定的 dispatch group 中的一个任务已经执行完成,减少该 group 的任务计数。在任务执行完成后调用dispatch_group_leave
,表示一个任务执行完成。
使用 dispatch_group_enter
和 dispatch_group_leave
函数的步骤如下:
- 创建一个 dispatch group。
- 在需要执行的任务开始前调用
dispatch_group_enter
。 - 在任务执行完成后调用
dispatch_group_leave
。 - 使用
dispatch_group_notify
函数设置一个回调块,在所有任务执行完成后执行额外的操作。
下面是一个示例代码,演示了如何使用 dispatch_group_enter
和 dispatch_group_leave
函数管理 dispatch group 中的任务计数:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_enter(group);
dispatch_async(queue, ^{// 任务1NSLog(@"Task 1 executed");dispatch_group_leave(group);
});dispatch_group_enter(group);
dispatch_async(queue, ^{// 任务2NSLog(@"Task 2 executed");dispatch_group_leave(group);
});// 设置一个回调块,在所有任务执行完成后执行额外的操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"All tasks executed");
});// 等待所有任务执行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
在上面的示例中,首先创建了一个 dispatch group,并向该 group 中添加了两个任务。在每个任务开始前调用 dispatch_group_enter
,在任务执行完成后调用 dispatch_group_leave
。最后使用 dispatch_group_notify
函数设置一个回调块,在所有任务执行完成后打印信息。
通过使用 dispatch_group_enter
和 dispatch_group_leave
函数,可以更灵活地管理 dispatch group 中的任务计数,实现对一组任务的控制和同步。
六、GCD其他用法
6.1 dispatch_once 只执行一次方法
dispatch_once
是 GCD(Grand Central Dispatch)中的一个函数,用于确保某个代码块只会被执行一次。在多线程环境下,dispatch_once
可以保证代码块只会在第一次调用时执行,后续的调用会被忽略。
dispatch_once
函数的原型如下:
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
使用方法如下:
- 定义一个全局的
dispatch_once_t
变量,用于标记代码块是否已经执行过。 - 调用
dispatch_once
函数,传入上述变量和要执行的代码块。
示例代码如下:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{// 只会执行一次的代码块NSLog(@"This will only be executed once");
});
dispatch_once
的特点和用法如下:
- 线程安全:
dispatch_once
是线程安全的,可以在多线程环境下正确地保证代码块只会执行一次。 - 性能高效:
dispatch_once
使用了一种高效的方式来实现只执行一次的功能,避免了不必要的性能开销。 - 适用场景:适用于需要在程序运行过程中只执行一次的初始化代码,比如单例模式的创建。
总之,dispatch_once
是一个非常实用的 GCD 函数,可以帮助我们实现一次性初始化等场景下的代码保护和线程安全操作。
6.2 dispatch_after 延迟执行方法
dispatch_after
是 GCD(Grand Central Dispatch)中的一个函数,用于延迟执行任务。通过 dispatch_after
函数,我们可以在指定的时间后执行一个代码块,从而实现延迟执行的效果。
dispatch_after
函数的原型如下:
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
使用方法如下:
- 创建一个
dispatch_time_t
对象,用于指定延迟的时间。可以使用dispatch_time
函数来创建,也可以使用dispatch_time_delay
函数来指定延迟的秒数。 - 调用
dispatch_after
函数,传入延迟时间、执行任务的队列和要执行的代码块。
示例代码如下:
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{// 2秒后在主队列中执行的代码块NSLog(@"This will be executed after 2 seconds");
});
dispatch_after
的特点和用法如下:
- 灵活性:可以精确控制代码块的延迟执行时间。
- 可读性:通过使用
dispatch_time
函数创建延迟时间,可以清晰地表达延迟的时长。 - 适用场景:适用于需要延迟执行任务的场景,比如实现延迟加载、动画效果等。
总之,dispatch_after
是一个方便实用的 GCD 函数,可以帮助我们实现延迟执行任务的需求,提升代码的灵活性和可读性。
6.3 dispatch_barrier_async
栅栏函数
dispatch_barrier_async
是 GCD(Grand Central Dispatch)中的一个函数,用于向指定的并发队列中提交一个 barrier 任务。Barrier 任务会等待在它之前提交的任务执行完成后才会执行,并且会等待它自己的任务执行完成后再继续执行后续的任务。
dispatch_barrier_async
函数的原型如下:
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
使用方法如下:
- 创建一个自定义的并发队列,确保队列是通过
dispatch_queue_create
函数创建的,并且设置为并发队列。 - 调用
dispatch_barrier_async
函数,传入自定义的并发队列和要执行的 barrier 任务代码块。
示例代码如下:
dispatch_queue_t customQueue = dispatch_queue_create("com.example.barrierQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_barrier_async(customQueue, ^{// barrier 任务代码块NSLog(@"Barrier task executed");
});
dispatch_barrier_async
的特点和用法如下:
- 保证顺序:保证 barrier 任务会在并发队列中的其他任务执行完成后才会执行,且 barrier 任务执行完后才会继续执行后续的任务。
- 数据同步:适用于需要在读写数据时保证数据同步的场景,可以避免读写数据时出现竞争条件。
- 性能优化:可以提高并发队列中读写操作的性能,确保写操作不会受到读操作的影响。
总之,dispatch_barrier_async
是一个非常有用的 GCD 函数,可以帮助我们实现在并发队列中执行 barrier 任务,从而保证任务的顺序和数据的同步性。
6.4 dispatch_semaphore
信号量
在 Objective-C 中,GCD(Grand Central Dispatch)提供了 dispatch_semaphore
信号量来控制并发访问的线程数量。dispatch_semaphore
可以用来实现线程同步和资源管理,允许指定数量的线程同时访问共享资源。
以下是 dispatch_semaphore
的介绍和使用方法:
(1) dispatch_semaphore
介绍
dispatch_semaphore
是一个信号量,用于控制并发访问的线程数量。
它有两个主要操作:dispatch_semaphore_wait
和 dispatch_semaphore_signal
。
dispatch_semaphore_wait
:当信号量的值大于等于 1 时,会将信号量的值减 1,并立即返回。如果信号量的值为 0,则会阻塞当前线程,直到有其他线程调用dispatch_semaphore_signal
使信号量的值增加为非零。dispatch_semaphore_signal
:将信号量的值加 1,唤醒一个被阻塞在dispatch_semaphore_wait
上的线程。
(2) 使用示例:
下面是一个示例代码,演示了如何使用 dispatch_semaphore
控制并发访问的线程数量:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // 创建信号量,初始值为 1
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);for (int i = 0; i < 5; i++) {dispatch_async(queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量NSLog(@"Task %d started", i);sleep(1); // 模拟任务执行NSLog(@"Task %d finished", i);dispatch_semaphore_signal(semaphore); // 释放信号量});
}
在上面的示例中,首先创建了一个初始值为 1 的 dispatch_semaphore
。然后使用 dispatch_async
在全局队列中执行了 5 个任务,每个任务在开始时调用 dispatch_semaphore_wait
等待信号量,表示占用一个资源;在任务执行完成后调用 dispatch_semaphore_signal
释放信号量,表示释放资源。
通过使用 dispatch_semaphore
,可以实现对共享资源的并发访问控制,限制同时访问资源的线程数量,从而避免竞态条件和数据竞争问题。
相关文章:

iOS面试:4.多线程GCD
一、多线程基础知识 1.1 什么是进程? 进程是指在系统中正在运行的一个应用程序。对于电脑而已,你打开一个软件,就相当于开启了一个进程。对于手机而已,你打开了一个APP,就相当于开启了一个进程。 1.2 什么是线程&am…...

云计算计算资源池与存储池访问逻辑
在云计算环境中,计算资源池和存储池通常是分开管理和访问的。计算资源池包含了用于运行虚拟机的 CPU、内存等计算资源,而存储池则提供了用于存储虚拟机镜像、数据等的存储资源。 计算资源池和存储池之间通常通过网络进行访问,它们之间不存在直…...

【Linux】部署单机项目(自动化启动)---(图文并茂详细讲解)
目录 一 准备工作 1.1 连接服务器拷贝文件 1.2 解压 二 JDK安装 2.1 配置坏境变量 2.2 查看版本 三 Tomcat(自启动) 3.1 复制启动命令的位置 3.2 添加命令相关配置文件 3.2.1 配置jdk及tomcat目录 3.2.2 添加优先级 3.3 设置自启动命令 3.4 开放端口 四 My…...

修复Microsoft Edge WebView2无法安装的问题
修复Microsoft Edge WebView2无法安装的问题 场景解决方案 场景 系统:win11 电脑:联想14 前提:使用Geek Uninstaller强制删除了Microsoft Edge WebView2 同时下载了clash verge。 发现根本无法运行(点击了无任何反应且图标颜色…...

Linux命令-chgrp命令(用来变更文件或目录的所属群组)
说明 chgrp命令 用来改变文件或目录所属的用户组。该命令用来改变指定文件所属的用户组。其中,组名 可以是用户组的id,也可以是用户组的组名。文件名可以 是由空格分开的要改变属组的文件列表,也 可以是由通配符描述的文件集合。如果用户不是…...

linux下搭建boost、muduo、mysql、nginx
参考博客: 1.boost:C网络编程 - Boost::asio异步网络编程 - 01- boost库源码编译安装 2.muduo:C muduo网络库知识分享01 - Linux平台下muduo网络库源码编译安装 3.mysql: sudo apt-get install mysql-server sudo apt-get inst…...

java基础-List常用方法
目录 常用方法逆序升序List<自定义类>排序List删除元素List转String数组List的add函数查找一个,分隔的字符串中是否有某值根据.分割字符串根据空格分隔字符串 常用方法 逆序 Collections.reverse(List) 升序 Collections.sort(List) List<自定义类>排序 首先…...

Android 如何添加自定义字体
Android 如何添加自定义字体 比如我要添加 jetbrains 相关字体 在 res 文件夹中添加 font 文件夹。里面放入你的字体文件 .ttf .otf,字体文件名需要是小写,只能是字母和下划线。 在 xml 布局文件中直接通过 android:fontFamily"font/jetbrainsmo…...

MacOs 围炉夜话
文章目录 一、安装 Mac 一、安装 Mac macOS是一套由苹果开发的运行于Macintosh系列电脑上的操作系统。macOS是首个在商用领域成功的图形用户界面操作系统。 VM虚拟机怎么安装mac os?(全教程) 虚拟机:VMware Workstation 17 pro W…...

爬取数位观察城市数据知识总结
# 抓取数位观察中城市的GDP,公交车数量,户籍人口 # url "https://www.swguancha.com/home/query-city-page" # 1.找数据 # 1.1如果数据在页面源代码里,则访问,在本案例中并没有在源代码中 # 1.2如果数据不在页面源代码里ÿ…...

[About-C++] 非常实用的知识点
往期推荐: [龙年第一更]专门说递归-CSDN博客 洛谷P1427 小鱼的数字游戏--------C中的栈与队列-CSDN博客 (并不华丽的分割线) 进入正文 一,自定义函数 类型 C中自定义函数(以下简称“函数”)只有两种&…...

渗透工具——kali中wpscan简介
一、什么是wpscan 1、常用于做用户名枚举爆破 2、WPScan是一个扫描 WordPress 漏洞的黑盒子扫描器,它可以为所有 Web 开发人员扫描 WordPress 漏洞并在他们开发前找到并解决问题。我们还使用了 Nikto ,它是一款非常棒的Web 服务器评估工具,…...

信息安全计划:它是什么、为什么需要一个以及如何开始
每个组织都需要一个信息安全计划,因为数据已成为世界上最有价值的商品。与所有珍贵的东西一样,数据受到管理机构的严格监管,并且受到每个人(包括骗子)的觊觎。这就是网络犯罪不断增加的原因——与日益严格的合规环境同…...

【软件测试】定位前后端bug总结+Web/APP测试分析
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、Web测试中简单…...

Github 2024-02-21 开源项目日报 Top10
根据Github Trendings的统计,今日(2024-02-21统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目8非开发语言项目1TypeScript项目1 gpt4free 语言模型集合改进计划 创建周期:300 天开…...

机器学习模型的过拟合与欠拟合
机器学习模型的训练过程中,可能会出现3种情况:模型欠拟合、模型正常拟合与模型过拟合。其中模型欠拟合与模型过拟合都是不好的情况。下面将会从不同的角度介绍如何判断模型属于哪种拟合情况。 (1)欠拟合与过拟合表现方式 欠拟合…...

华为OD机试真题-虚拟游戏理财-2023年OD统一考试(C卷)---Python3--开源
题目: 考察内容: for if max 代码: """ 题目分析:投资额*回报率投资回报 要在可接受范围内选择最优的投资方式获得最大回报最多投资2个理财产品输入: 产品数int; 总投资额int; 总风险int 产品投资…...

新手搭建服装小程序全攻略
随着互联网的快速发展,线上购物已经成为了人们日常生活中不可或缺的一部分。服装作为人们日常消费的重要品类,线上化趋势也日益明显。本文将详细介绍如何从零开始搭建一个服装小程序商城,从入门到精通的捷径,帮助你快速掌握小程序…...

设计模式学习笔记 - 面向对象 - 3.面向对象比面向过程有哪些优势?面向过程真的过时了吗?
简述 在过往的工作中,我发现很多人搞不清面向对象和面向过程的区别,总认为使用面向对象编程语言来开发,就是在面向面向对象编程了。而实际上,他们只是在用面向对象编程语言,编写面向过程风格的代码而已,并…...

联想开天昭阳N4620Z笔记本如何恢复出厂麒麟操作系统(图解)
联想开天昭阳N4620Z笔记本简单参数: 中央处理器:KX-6640MA G2 内存:8GB 固态硬盘:512GB SSD 显示器:14.0”FHD 电池:4Cell 操作系统:麒麟KOS中文RTM(试用版) 此款笔…...

Qt经典面试之理论总结(自己整理总结)
目录 1、QT信号和槽的原理和理解 2、connect函数的参数,第五个参数是什么 1、QT信号和槽的原理和理解 信号和槽机制底层是通过函数间的相互调用实现的。 信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于&#…...

【YOLO系列算法人员摔倒检测】
YOLO系列算法人员摔倒检测 模型和数据集下载YOLO系列算法的人员摔倒检测数据集可视化数据集图像示例: 模型和数据集下载 yolo行人跌倒检测一: 1、训练好的行人跌倒检测权重以及PR曲线,loss曲线等等,map达90%多,在行人跌…...

获取淘宝商品详情API、商品主图、图片搜索api
获取淘宝详情API的方式有以下几种: 使用淘宝开放平台提供的接口:淘宝开放平台提供了多个API接口,让开发者可以通过接口获取商品详情信息。你可以到淘宝开放平台官网申请开发者账号,并查看相关接口文档,了解如何使用接…...

HarmonyOS创建一个ArkTS卡片
创建一个ArkTS卡片 在已有的应用工程中,创建ArkTS卡片,具体操作方式如下。 创建卡片。 根据实际业务场景,选择一个卡片模板。 在选择卡片的开发语言类型(Language)时,选择ArkTS选项,然后单…...

ChatGPT Plus遇到订阅被拒原因与解决方案
ChatGPT Plus被广泛认为相比普通版本更快、更强,并且能最先体验新功能。 很多小伙伴再订阅时遇到图片中的问题 错误提示包括这些: Your credit card was declined.Try paying with a debit card instead.您的信用卡被拒绝了。请尝试用借记卡支付。你的…...

UE蓝图 函数调用(CallFunction)节点和源码
系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 UE蓝图 返回结果(FunctionResult)节点和源码 UE蓝图 函数调用(CallFunction)节点和源码 文章目录 系列文章目录一、Call…...

Vue单文件学习项目综合案例Demo,黑马vue教程
文章目录 前言一、小黑记事本二、购物车三、小黑记账清单 前言 bilibili视频地址 一、小黑记事本 效果图 主代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"/><meta http-equiv"X-UA-Compatible&…...

机器视觉【3】非线性求解相机几何参数
线性求解相机几何参数的缺点 上一章节介绍学习了(DLT)线性求解相机几何参数,了解到线性求解法当中比较明显的缺点: 没有考虑到镜头畸变的影响不能引入更多的约束条件融入到DLT算法当中优化最关键的是,代数距离并不是…...

Qt编译报错:The slot requires more arguments than the signal provides.
编译时代码没有提示错误的地方,报错的地方在qt的文件,还以为什么莫名其妙的错误呢,原来就是连接的信号和槽函数参数不匹配,有个信号是没有参数的,但我的槽函数有个参数,然后就报错了。 改下槽函数的参数就…...

【Unity】提示No valid Unity Editor liscense found.Please active your liscense.
有两个软件,如果只有一个,点黑的不会有效果、、、、(楼主是这个原因,可以对号入座一下) 简而言之,就是去下载Unity Hub,再里面激活管理通行证 问题情境: 点击unity出现以下弹窗&a…...