iOS——【CGD】

GCD

什么是GCD

GCD指的是Grand Central Dispatch,它是苹果公司开发的一套多线程编程技术。GCD提供了一种简单而有效的方式来管理应用程序中的并发任务。它通过将任务提交到适当的队列(串行队列或并发队列)来管理并发执行的任务,从而帮助开发者更轻松地实现并发编程,提高应用程序的性能和响应速度。
使用GCD的好处:

  1. GCD可用于多核的并行运算
  2. GCD会自动利用更多的CPU内核(双核、四核)
  3. GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  4. 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

GCD源代码的例子:


dispatch_async(queue, ^{
/* 长时间处理
例如AR用画像识别、数据库访问*/

/* 长时间处理结束,主线程使用该处理结果。 */
  dispatch_async(dispatch_get_main_queue(), ^{ 
  /* 只在主线程可以执行的处理
     例如UI界面更新 */
  });
});

上面的就是在后合线程中执行长时间处理,处理结束时,主线程使用该处理结果的源代码。

这样,dispatch_async (queue, ^{ 这仅有 一行的代码表示让处理在后台线程中执行。
dispatch_async(dispatch_get_main_queue (), ^{ 仅此 一行代码就能够让处理在主线程中执行。

在导入GCD之前还有performSelector系列方法来实现多线程编程,但是在蓝书的学习中我们说过要多用GCD少用performSelector系列方法。详见:

多线程编程

首先认识几个基本概念:

  • 进程:
  1. 进程是一个具有一定独立功能的程序关于某次数据集合的一次运行活动,每个进程都有自己独立的一块内存空间,进程是操作系统分配资源的基本单元.
  2. 进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,我们可以理解为手机上 的一个 app
  3. 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内,拥有独立运行所需的全部资源。
  • 线程
  1. 线程是进程中执行运算的最小单位,是进程中的一个实体。
  2. 线程是系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源。
  3. 一个进程至少有一个线程,应用程序启动的时候,系统会默认开启一条线程,也就是主线程。一个进程可以运行多个线程,多个线程可共享进程所拥有的全部资源。同一进程中的多个线程之间可以并发执行。
  • 线程与进程的关系
  1. 线程是进程的执行单元,进程的所有任务都在线程中执行
  2. 线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
  3. 一个程序可以对应多个进程(多进程),一个进程中可有多个线程,但至少要有一条线程
  4. 同一个进程内的线程共享进程资源

源代码实际上在Mac 或iPhone 上是如何执行的呢?
源代码通过编译器转换为CPU命令列(二进制代码 ) 。
汇集CPU命令列和数据,将其作为一个应用程序安装到Mac或iPhone 上。启动该应用程序后,首先便将CPU命令列配置到内存中。CPU从应用程序指定的地址开始, 一个一个地执行CPU 命令列。就这样不断循环下去。
执行命令列的地址会远离当前的位置(位置迁移)。但是,由于一个CPU一次只能执行 一个命令,不能执行某处分开的并列的两个命令,因此通过CPU执行的CPU命令列就好比一条无分叉的大道,其执行不会出现分歧。
“一条无分叉的大道〞即为“线程”。这种无分叉路径不只1条,存在有多条时即为“ 至线程”。

1个CPU核一次能够执行 的CPU 命令始终为1。那么怎样才能在多条路径中执行CPU 命令列呢?

在具有多个CPU 核的情况下,提供了多个CPU校并行执行多个线程的技术。 这种利用多线程编程的技术就被称为“ 多线程编程”。
但是,多线程编程实际上是 一种易发生各种问题的编程技术。比如多个线程更新相同的资源 会导致数据的不一致(数据竞争)、停止等待事件的线程会导致主个线程相互持续等待(死锁)、 使用太多线程会消耗大量内存等。

应用程序在启动时,通过最先执行的线程,即“ 主线程” 来描绘用户界面、处理触摸屏幕的 事件等。如果在该 主线程中进行长时间的处理,如AR 用画像的识别或数据库访问,就会妨碍 主 线程的执行(阻塞)。在oS ×和i0s 的应用程序中,会妨碍主线程中被称为RunLoop 的主循环 的执行,从而导致不能更新用户界面、应用程序的画面长时间停滞等问题。 这就是长时间的处理不在主线程中执行而在其他线程中执行的原因。

使用多线程编程,在执行长时间的处理时仍可保证用户界面的响应性能。

GCD的API

Dispatch Queue

首先回顾一 下苹果官方对GCD 的说明。

  • 开发者要做的只是定义想执行的任务井追加到适当的Dispatch Queue 中。
    这 句 话 用 源 代 码 表 示 如 下:
dispatchasync (queue, ^{
/* 想执行的任务*/
});

该源代码使用Block 语法“定义想执行的任务”,通过dispatch_async函数“追加” 赋值在变 量 queue的 “Dispatch Queue中 ” 。仅这样就可使指定的Block在另一线程中执行。
“Dispatch Queue ” 是执行处理的等待队列。应用程序编程人 员通过dispatch_async 函数等API,在Block 语法中记述想执行的处理并将其追加到Dispatch Queue 中。Dispatch Queue 按照追加的顺序 (先进先出)执行处理。
在执行处理时存在两种Dispatch Queue, 一种是等待现在执行中处理的Serial Dispatch Queue,另一种是不等待现在执行中处理的Concurrent Dispatch Queue。

当变量queue 为Concur rent Dispatch Queue 时, 不用等待处理结束,可以并行执行多个处理,但 并行执行的处理数量取决 于当前系 统 的 状 态 。 即基 于 Dispatch Queue中的处理数 、CPU核数以及CPU负荷等当前系统的状态来快定Concurrent Dispatch Queue 中并行执行的处理数。

在Concurrent DispatchQueue中执行处理时,执行顺序会根据处理内容和系统状态发 生改变。它不同于执行顺序固定的Serial Dispatch Queue。在不能改变执行的处理顺序或不想并 行执行多个处理时使用Serial DispatchQueue。

dispatch_queue_create

如何才能得到Dispatch Queue呢?第一种方法是通过GCD的API 生成Dispatch Queue,即dispatch_queue_create;

dispatch_queue_t myqueue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
  • 参数1:指定生成返回的Dispatch Queue的名称
    该名称在Xcode和Instruments的调试器中作为Dispatch Queue的名称表示,该名称也会出现在程序崩溃时所生成的CrashLog中;队列的名称推荐使用应用程序 ID 这种逆序全程域名。
  • 参数2:指定为NULL或DISPATCH_QUEUE_SERIAL,生成Serial Dispatch Queue

指定为DISPATCH_QUEUE_CONCURRENT,生成Concurrent Dispatch Queue
虽然Serial Dispatch Queue 和Concurrent Dispatch Queue受到系统资源的限制,但用dispatch_queue_create 函数可生成任意多个Dispatch Queue。
当生成多个Serial Dispatch Queue时,各个Serial Dispatch Queue 将并行执行。
一旦生成Serial DispatchQueue 并追加处理,系统对于一个Serial DispatchQueue 就只生成并使用一个线程。
因此只在为 了避免多线程编程问题之一一一 多个线程更新相同资源导致数据竞争时使用 Serial DispatchQueue。
当想并发执行不发生数据竞争等问题的处理时,使用 Concurrent Dispatch Queue
iOS6之前,Dispatch Queue 需要进行手动管理释放,现已纳入ARC管理范围。

生成Serial Dispatch Queue 时,像该源代码这样,将第二个参数指定为NULL。生成 Concurrent Dispatch Queue时 ,像下面源代码一样 , 指 定 为 DISPATCH_QUEUECONCURRENT。

dispatch_queue_t myqueue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_queuecreate 西数的返回值为表示Dispatch Queue 的 “dispatch queue t 类型”。在之 前源代码中所出现的变量queue 均为dispatch_queue_t 类型变量.

Main Dispatch Queue/Global Dispatch Queue

第二种方法是获取系统标准提供的Dispatch Queue。
Main DispatchQueve是在主线程中执行的Dispatch Queue。因为主线程只有1个,所以Main DispatchQueue 自然就是SerialDispatchQueue。
Global Dispatch Queue 有4个执行优先级,分别是高优先级、默认优先级、低优先级、后台优先级。

各种Dispatch Queue 的获取方法如下:

// Main Dispatch Queve的获取方法
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue ( ) 

//Global DispatchQueue(高优先级〕的获取方法
dispatch_queue_t globalDispatchQueueHigh =
dispatch_get_global_queue(DISPATCHQUEUEPRIORITY_HIGH, 0);

//Global DispatchQueue(默认优先级)的获取方法 
dispatch_queue_t globalDispatchQueueDefault =
dispatch_get_global_queue(DISPAICH_QUEUE_PRIORITY_DEFAULT,0);

//Global Dispatch Queue (低优先级)的获取方法 
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

//Global Dispatch Queue(后台优先级)的获取方法
dispatch_queue_t globalDispatchQueueBackground =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,
0) :

dispatch_set_target_queue

dispatch_queue_create 两数生成的Dispatch Queue 不管是Serial Dispatch Queue 还是Concurrent Dispatch Queue,都使用与默认优先级Global Dispat ch Queue 相同执行优先级的线程。

SerialDispatchQueue 的生成方法如下:

dispatch_queue_t mySerialDispatchQueue =
dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0); 
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

将Dispatch Queue 指定为dispatch_settargetqueue 函数的参数,不仅可以变更Dispatch Queue 的执行优先级,还可以作成DispatchQueue 的执行阶层。如果在多个Serial DispatchQueue 中用 dispatch_set_target_queue两数指定目标为某一个SerialDispatchQueue,那么原先本应并行执行的 多个Serial Dispatch Queue,在目标Serial Dispatch Queue 上只能同时执行一个处理。

dispatch_after

这种想在指定时间 后执行处理的情况,可使用dispatch_after 两数来实现。
在 3 秒 后 将 指 定 的 B l o c k 追 加 到 M a i n D i s p a t c h Q u e u e 中 的 源 代 码 如 下:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC); 
dispatch_after(time, dispatch_getmain_queue(), ^{
  NSLog (@"waited at least three seconds."); 
});

dispatch_time 函数能够获取从第一 个参数dispatch_time_t 类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。第一个参数经常使用的值是之前源代码中出现的 DISPATCH_TIME_NOW。这表示现在的时间。即以 下源代码可得到表示从现在开始 1 秒后的 dispatch time t 类型的值。

dispatch_time_t time =dispatch_time(DISPATCHL_TIME_NOW, 1u11*NSEC_PER_SEC);

数值和NSEC_PERSEC的乘积得到单位为毫微秒的数值。“ull” 是C语言的数值字面量, 是 显 式 表 明 类 型 时 使 用 的 字 符 串 (表 示 “ unsigned long long ” ) 。

Dispatch Group

在追加到Dispatch Queue 中的多个处理全部结束后想执行结束处理,这种情况会经常出现。 只使用 一个Serial Dispatch Queue时,只要将想执行的处理全部追加到该Serial Dispatch Queue 中并在最后追加结束处理,即可实现。但是在使用Concurrent Dispatch Queue 时或同时使用多个 Dispatch Queue 时,源代码就会变得颇为复杂。
在此种情况下使用Dispatch Group。
例:追加了个Block到Global Dispatch Queue,这些Block 如果全部执行完牛,就会执行Main Dispatch Queue中结束处理用的Block。

dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue,^{
  NSLog(@"b1k0”);});  
dispatch_group_async(group, queue, ^{
  NSLog (@"blk1”);});
dispatch_group_async(group, queue, ^{
  NSLog (@"b1k2”);});
dispatch_group_notify (group,dispatch_get_main_queue (), ^{NSLog (@"done”);});
dispatch_release(group);

因为向Global DispatchQueue 即Concurrent DispatchQueue 追加处理,多个线程并行执 行,所以追加处理的执行顺序不定。执行时会发生变化,但是此执行结果的done 一定是最后输 出的。
无论向什么样的Dispatch Queue 中追加处理,使用Dispatch Group 都可监视这些处理执行的 结束。一旦检测到所有处理执行结束,就可将结束的处理追加到Dispatch Queue 中。这就是使用 Dispatch Group 的原因。

在DispatchGroup 中也可以使用dispatch_group_wait 两数仅等待全部处理执行结束:

dispatch_group_wait(group, DISPATCH_TIME_FOREVER); dispatch_release(group);

指定DISPATCH_TIME NOW,则不用任何等待即可判定属于Dispatch Group 的处理是否执行结束。

long result = dispatch_group_wait(group,DISPATCH_TIME_NOW);

dispatch_barrier_async

在访问数据库或文件时,如前所述,使用Serial Dispatch Queue 可避免数据亮争的问题。
写入处理确实不可与其他的写入处理以及包含读取处理的其他某些处理并行执行。但是如果 读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。

虽然利用DispatchGroup和dispatchsettarget queue两数也可实现,但是源代码会很复杂。 GCD 为我们提供 了更为聪明的解决方法——- dispatch barrier_async 两数。该函数同dispatch queue_create函数生成的Concurrent DispatchQueue一起使用。
首先dispatch queue create 函数生成Concurrent Dispatch Queue,在dispatch_async 中追加读取处理。

dispatch_queue_t queue = dispatch_queue_create (
"com. example.god. ForBarrier", DISPATCH_QUEUE_CONCURRENT) :
dispatch_async (queue, blk0_for_reading); dispatch_async(queue, blklf o rreading);
dispatch_async (queue, b1k2_for_reading) ; dispatch_async (queue, b1k3_for_ reading);
dispatch_async (queue, bik4_for_reading) ; 
dispatch_async(queue, bik5f o rreading);
dispatch_async (queue, blk6_for_reading) ; dispatch_async (queue, blk7_for_reading);
dispatch_release (queue);

在blk3_for_reading 处理和blk4_for_reading 处理之间执行写入处理,并将写入的内容读取 blk4_for_reading 处理以及之后的处理中。

dispatch_async ( queue, b1k0_for_reading) ;
dispatch async (queue, blk1 f o rreading) ;
dispatch_async (queue, b1k2_for_reading) ; dispatchasync (queue, bik3f o rreading)
/*
* 写入处理
* 将写入的内容读取之后的处理中 */
dispatch_async ( queue, b1k4_for_reading) ;
dispatch_async ( queue, b1k5_for_reading);
dispatch_async (queue, b1k6_for_reading); dispatch_async (queue, b1k7_for_ reading);

这时我们要使用dispatch_barrier_async 函数。dispatch barrier_async 函数会等待追加到 Concurent Dispatch Queue 上的并行执行的处理全部结束之后,再将指定的处理追加到该 Concurrent DispatchQueue 中。然后在由dispatch_barrier_async 函数追加的处理执行完牛后, Concurrent Dispatch Queue 才恢复为 一般的动作,追加到该Concurrent DispatchQueue 的处理又开 始并行执行。

dispatch_async(queue, bik0_for_reading): 
dispatch_async (queue, blk1_for_reading) ;
dispatch_async (queue, b1k2_for_reading);
dispatch_async (queue, b1k3_for_reading); dispatch_barrier_async(queue, blk_for_writing);
dispatch_async (queue, bik4_for_reading); 
dispatch_async (queue, blk5_for_reading) ;
dispatch_async (queue, blk6_for_reading); dispatch_async (queue, b1k7_for_reading);

使用Concurrent Dispatch Queue 和dispatch barrier_async 函数可实现高效率的数据库访问和 文件访问。

dispatch_sync

dispatch async函数的“async” 意味着“非同步”(asynchronous),就是将指定的Block“非同步” 地追加到指定的Dispatch Queue中。dispatch_async 函数不做任何等待。
既然有“async”,当然也就有“sync”,即dispatch_sync 函数。它意味着“ 同步”, 也 就 是 将 指 定 的 B l o c k “ 同 步 ” 追 加 到 指 定 的 D i s p a t c h Q u e u e 中 。 在 追 加 B l o c k 结束之前,dispatch_sync 两数会 一直等待。
“ 等 待 〞 意 味 着 当 前 线 程 停 止 。
假设执行Main Dispatch Queue 时,使用另外的线程Global Dispatch 进行处理,处理结束后立即使用所得到的结果。在这种情况下就要使用dispatch sync函数。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
  // 处理
};

一旦调用dispatch_sync 两数,那么在指定的处理执行结束之前,该函数不会返回。dispatch sync两数可简化源代码,也可说是简易版的dispatch _group_wait 函数。
正因为dispatch sync 两数使用简单,所以也容易引起问题,即死锁。
例如如果在主线程中执行以下源代码就会死锁。

dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{NSLog (@"Hello?);});

该源代码在Main Dispatch Queue 即主线程中执行指定的Block,并等待其执行结束。而其实在主线程中正在执行这些源代码,所以无法执行追加到Main DispatchQueue的Block。下面例子也一样。

dispatch_queue_t queue = dispatch_get_main_queue () ; dispatch_async (queue, ^{
dispatchsync (queue, ^{NSLog (@"Hello?");});
});

MainDispatchQueue中执行的Block 等待MainDispatch Queue 中要执行的Block 执行结束。

Serial Dispatch Queue 也会引起相同的问题。

dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue”,NULL);
dispatch_async(queue, ^{
  dispatch_sync (queue, ^ (NSLog (@"Hello?"); }); 
}):

相应的也有dispatch_barrier_ sync 函数,它会等待追加处理的执行结束。

dispatch_apply

dispatch_apply 函数是dispatch_sync 函数和Dispatch Group 的关联API 。该函数按指定的次数将指定的Block追加到指定的Dispatch Queue 中,并等待全部处理执行结束。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index){
  NSLog (@"&zu", index);
});
NSLog(@“done”);

该代码的执行结果是:4 1 0 3 5 2 6 8 9 7 done
因为在Global Dispatch Queue 中执行处理,所以各个处理的执行时间不定。但是输出结果中 最 后 的 d o n e 必 定 在 最 后 的 位 置 上。 这 是 因 为dispatch_apply函数 会 等 待 全 部 处 理 执 行 结 束 。

由于dispatch_apply 函数也与dispatch_sync 函数相同,会等待处理执行结束,因此推荐在dispatch_async 函数中非同步地执行dispatch_apply 函数。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_ DEFAULT, 0);
//在Global DispatchQueve中非同步执行
dispatch_async(queue, ^{
  //Global Dispatch Oueue
  //等待dispatch_apply函数中全部处理执行结束
  dispatch_apply ([array count], queue, ^(size_t index){
    //并列处理包含在NSAr r a y 对象的全部对象
    NSLog(@"&zu: %@", index, [array objectAtIndex:index]);
  });
// dispatch apply西数中的处理全部执行结束
//在Main Dispatch Queue中非同步执行
  dispatch_async(dispatch_get_main_queue(), ^{
//在 Ma i n D i s p a t c h Q u e u e 中 执 行 处 理,用户界面更新等
  NSLog ( @"done" ) ;
  });
});

dispatch_suspend / dispatch_resume

dispatch_suspend函数用于挂起不希望执行的Dispatch Queue:

dispatch_suspend(queue);

dispatch_resume函数用于恢复被挂起的Dispatch Queue:

dispatch_resume(queue);

这些函数对己经执行的处理没有影响。挂起后,追加到Dispatch Queue 中但尚末执行的处理 在此之后停止执行。而恢复则使得这些处理能够继续执行。

Dispatch Semaphore

Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数为1或者大于1时,计数减一而不等待。
使用方法:
使用dispatch_semaphore_create函数生成dispatch_semaphore_t类型的Dispatch Semaphore。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

参数表示的是计数的初始值。
该函数课通过dispatch_retain持有,通过dispatch_release释放。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值达到1或者等于1。当计数值大于等于1,或者在待机中计数值大于等于1时,对该计数进行减法并从dispatch_semaphore_wait函数返回。可通过函数返回值进行达到一些希望的目的和操作。

dispatch_time_t time =dispatch_time( DISPATCH_TIME_NOW, 1U11 * NSEC_PER_SEC); 
long result = dispatch_semaphore_wait(semaphore, time);
if(result ==0){
/*
由于Dispatch Semaphore的计数值达到大于等于1或者在待机中的指定时间内
* Dispatch Semaphore的计数值达到大于等于1
*所以Dispatch Semaphore的计数值减去1。
*可执行需要进行排他控制的处理*/
} else {
/*
*由于Dispatch Semaphore的计数值为0*因此在达到指定时间为止待机
*/
}

dispatch_semaphore_wait函数返回0时,可安全地执行需要进行排他控制的处理。该处理结束时通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数值加1。

dispatch_once

dispatch_once 函数是保证在应用程序执行中只执行一次指定处理的API。
使用dispatch once 函数,源代码写为:

static dispatch_once_t pred;
dispatch_once(&pred, ^{
  // 初始化
});

通过dispatch_once 函数,该源代码即使在多线程环境 下执行,也可保证百分之百安全。

Dispatch I/O

现今的输入/输出硬件己经可以做到一 次使用多个线程更快地并列读取了。能实现这一功能的就是Dispatch I/O 和Dispatch Data。如果想提高文件读取速度,可以尝试使用Di spatch I/O。

GCD 实现

Dispatch Queue

DispatchQueue究竟是如何实现的呢?

  • 用 于管 理 追 加 的 B l o c k 的 C 语 言 层 实 现 的 F I F O 队 列
  • Atomic 两数中实现的用 于排他控制的轻量级信号
  • 用 于 管 理 线 程 的 C 语 言 层 实 现 的 一些 容 器

通常,应用程序中编写的线程管理用的代码要在系統级实现。

使 用 G C D 要 比 使 用一般 的 多 线 程 编 程 A P I 更 好 。 并 且 , 如 果 使 用GCD 就不必编写为操作线程反复出现的类似的源代码 (这被称为固定源代码片断),而可以在 线程中集中实现处理内容, 真的是好处多多。我们尽量多使用GCD 或者使用 了Cocoa 框架GCD 的NSOperationQueue 类等API.

Global DispatchQueue 有如下8种。
• Global Dispatch Queue (High Priority)
• Global Dispatch Queue (Default Priority) • Global Dispatch Queue (Low Priority)
• Global Dispatch Queue (Background Priority)
• Global Dispatch Queue (High Overcommit Priority)
• Global Dispatch Queue (Default Overcommit Priority) • Global Dispatch Queue (Low Overcommit Priority )
• Global Dispatch Queue (Background Overcommit Priority)
优先级中附有Overcommit 的Global Dispatch Queue 使用在Serial Dispatch Queue 中。如 Overcommit 这个名称所示,不管系统状态如何,都会强制生成线程的DispatchQueuc。 这8种Gilobal DispatchQueue各使用1个ptbread_workqueue。GCD初始化时,使用pthread_workqueue _create _np 两 数 生成 pthread_workqueue。
pthread_workqueue 包含在Libe提供的pthreads API 中。其使用bsdthreadr egister 和worka open 系统调用,在初始化 XNU 内核的workqueue 之后获取workqueue 信息。

XNU 内核持有4 种workqueue 。
• WORKQUEUE HIGHPRIOQUEUE
• WORKQUEUE DEFAULTPRIOQUEUE
• WORKQUEUE_ LOW PRIOQUEUE • WORKQUEUE_BG_PRIOQUEUE
以上为4 种执行优先级的workqueue。该执行优先级与Global Dispatch Queue 的4 种执行优 先级相同 。

Dispatch Source

Dispatch Source是BSD系内核惯有功能kqueue的包装。kqueue是在XNU内核中发生各种事件时,在应用程序编程方执行处理的技术。其CPU符合非常小,尽量不占用资源。
Dispatch Source可处理以下事件:

在这里插入图片描述

Dispatch Source与Dispatch Queue的不同是,后者可以将追加的执行处理取消,而前者则不能。

实际 上Dispatch Queue没有“取消” 这一概念。一旦将处理追加到DispatchQueue 中,就没有方法可将该处理去除,也没有方法可在执行中取消该处理。编程人员要么在处理中导入取消这一概念,要么放弃取消,或者使用NSOperationQueue 等其他方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/490980.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MQTT.fx和MQTTX 链接ONENET物联网提示账户或者密码错误

参考MQTT.fx和MQTTX 链接ONENET物联网开发平台避坑细节干货。_mqttx和mqttfx-CSDN博客 在输入password和username后还是提示错误,是因为在使用token的时候,key填写错误,将设备的密钥填入key中

【javaWeb 第四篇】后端-Maven详细入门教程

Maven入门教程 前言Maven配置Maven依赖管理Maven的生命周期 前言 本文是作者通过学习过程中的学习笔记,希望帮助大家,同时大家可以搭配黑马程序员进行学习 Maven配置 Maven是apache旗下的一个开源项目,是一款用于管理和构建java项目的工具…

Day48:WEB攻防-PHP应用文件上传中间件CVE解析第三方编辑器已知CMS漏洞

目录 PHP/ASP-中间件-上传相关-IIS&Apache&Nginx(解析漏洞) IIS Apache Nginx PHP-编辑器-上传相关-第三方处理引用 PHP-CMS源码-上传相关-已知识别到利用 知识点: 1、PHP-中间件-文件上传-CVE&配置解析 2、PHP-编辑器-文件上传-第三方引用安全 3…

单链表增序排列节点(单链表算法库拓展v2.0)

对单链表中元素进行排序(至少有2个数据节点) /************************************************** (13)函数名:LinkList_sorting 功 能:对单链表中元素进行排序(至少有2个数据节点) 参 数:LinkList *&L:要进行排序的单链表 注意: ① 空表,或者只有一个数据节点,则不需要…

企业级快速开发框架 nbsaas-boot 1.1.8-2024 发布了

<parent><groupId>com.nbsaas.boot</groupId><artifactId>nbsaas-boot</artifactId><version>1.1.8-2024</version> </parent> 本次更新内容 1. 重构代码生成器&#xff0c;采用类提取和字段提取两种方式&#xff0c;提取功能…

查看 Debian 系统版本的 6 种方式

本篇文章将为大家介绍 6 种查看 Dibian 系统发行版本号的方式。 1. 使用 lsb_release 命令 lsb_release 命令可用于查看 Linux 发行版操作系统的具体版本。它可能尚未安装在你的操作系统中&#xff0c;因此你需要先安装它。运行以下命令来安装 lsb_release&#xff1a; apt-…

云原生靶场kebernetesGoat、Metarget

靶场 文章目录 靶场kebernetesGoat靶场安装Docker in DockerSSRF漏洞容器逃逸到主系统Docker CIS 基线分析Kubernetes CIS 安全基线分析分析被部署挖矿软件的容器镜像获取环境信息Hidden in layersRBAC最低权限配置错误使用 Sysdig Falco 进行运行时安全监控和检测 Metarget ke…

MT6762_联发科MTK6762安卓核心板规格参数

MTK6762核心板是一款集成了蓝牙、fm、wlan和gps模块的高度集成基带平台&#xff0c;为LTE/LTE-A和C2K智能手机应用程序提供支持。该安卓核心板集成了ARM Cortex-A53处理器&#xff0c;工作频率可达2.0GHz&#xff0c;并且还集成了功能强大的多标准视频编解码器。除此之外&#…

VScode 内存溢出 yarn/npm内存溢出问题解决

当前目录下执行 increase-memory-limit 然后再启动

优化卡顿实力派,品质表现更出彩

游戏卡顿无疑是开发者最需要关注的重要性能问题之一。它影响玩家的游戏进程&#xff0c;并直接对玩家的游戏体验产生不良的影响。为助力开发者应对这一难题&#xff0c;在之前的版本中&#xff0c;卡顿分析页已经陆续推出了重点函数分析、卡顿点分析以及Timeline等功能&#xf…

C# wpf 嵌入winform控件

WPF Hwnd窗口互操作系列 第一章 嵌入Hwnd窗口 第二章 嵌入WinForm控件&#xff08;本章&#xff09; 第三章 嵌入WPF控件 文章目录 WPF Hwnd窗口互操作系列前言一、导入WinForm1、.Net Framwork&#xff08;1&#xff09;、右键添加引用&#xff08;2&#xff09;、勾选程序集…

DMA知识

提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 2024年3月26日23:32:43 今天看了DMA存储器到存储器的DMA传输和存储器到外设的DMA实验&#xff0c;在keil仿真可以看到效果。还没有在protues和开发…

Spire.PDF for .NET【文档操作】演示:查找并删除 PDF 中的空白页

PDF 中的空白页并不罕见&#xff0c;因为它们可能是作者故意留下的或在操作文档时意外添加的。当您阅读或打印文档时&#xff0c;这些空白页可能会很烦人&#xff0c;因此可能非常有必要将其删除。在本文中&#xff0c;您将了解如何使用Spire.PDF for .NET以编程方式查找和删除…

CODESYS和AB的PLC走ETHERNET/IP

添加Ethernet->添加EtherNet_IP_Adapter_1->EtherNet_IP_Module 添加数据&#xff1a;需要发送多少就写多少 填写数量类型&#xff1a; 2.导出EDS文件&#xff0c;此处导出EDS文件需修改版本号&#xff0c;此处版本号不能与AB库中从站版本号存在冲突&#xff0c;否则在…

Go语言学习Day4:函数(上)

名人说&#xff1a;莫愁千里路&#xff0c;自有到来风。 ——钱珝 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、函数的概念与定义①函数的概念②函数的具体定义③多返回值 2、函数参数与作用域①可变参数②形…

windows安装tomcat

安装之前需要安装jdk1.8可以参考windows安装jdk1.8-CSDN博客 一、下载tomcat Apache Tomcat - Apache Tomcat 8 Software Downloads 解压到D盘的D:\Program Files\tomcat目录下 二、配置环境变量 电脑右键属性-高级系统设置-高级-环境变量 1、在系统变量配置CATALINA_HOME环…

[flask]请求全局钩子

flask从入门到精通之钩子、异常、context、jinjia模板、过滤器 - 异步非阻塞 - 博客园 (cnblogs.com) 参考的这个博客&#xff0c;但有一个需要注意的是&#xff0c;最新版本的flask不知道是不是更新了还是怎么了&#xff0c;他的before_first_request不见了&#xff0c;如果继…

鸿蒙HarmonyOS 开发如果实现多端协同?

多端协同流程 多端协同流程如下图所示。 图1 多端协同流程图 约束限制 由于“多端协同任务管理”能力尚未具备&#xff0c;开发者当前只能通过开发系统应用获取设备列表&#xff0c;不支持三方应用接入。 多端协同需遵循 分布式跨设备组件启动规则 。 为了获得最佳体验&…

迭代实现二叉树的遍历-算法通关村

迭代实现二叉树的遍历-算法通关村 理论上&#xff0c;递归能做的迭代一定能做&#xff0c;但可能会比较复杂。有时候面试官要求不使用递归实现三种遍历&#xff0c;递归就是每次执行方法调用都会先把当前的局部变量、参数值和返回地址等压入栈中&#xff0c;后面在递归返回的时…

uniapp 使用命令行创建vue3 ts 项目

命令行创建 uni-app 项目&#xff1a; vue3 ts 版 npx degit dcloudio/uni-preset-vue#vite-ts 项目名称注意 Vue3/Vite版要求 node 版本^14.18.0 || >16.0.0 如果下载失败&#xff0c;请去gitee下载 https://gitee.com/dcloud/uni-preset-vue/repository/archive/vite-ts…