多线程-GCD

1、GCD好处

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

2、GCD任务和队列

任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码,在GCD中放在block中的。
执行任务有两种方式: 同步执行(sync)、 异步执行(async)
两者主要区别是: 是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
  • 同步执行(sync)
同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力。
  • 异步执行(async)
异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
eg:你要打电话给皮皮和水水。
同步执行就是,你打电话给皮皮的时候,不能同时打给水水,等到给皮皮打完了,才能打给水水(等待任务执行结束)。而且只能用当前的电话(不具备开启新线程的能力)。
而异步执行就是,你打电话给皮皮的时候,不等和皮皮的通话结束,还能直接给水水打电话,不用等着和皮皮通话结束再打(不用等待任务执行结束)。除了当前电话,还可以使用其他能使用的电话(具备开启新线程的能力)。
注意: 异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新的线程。这跟任务所指定的队列类型有关(下面会讲)。
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线程表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的尾部,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:
在GCD中有两种队列: 串行队列并发队列。两者都符合FIFO的原则。
两者最主要的区别是: 执行的顺序不同,以及开启线程数不同
  • 串行队列(Serial Dispatch Queue):
每次只有一个任务被执行。让任务一个接着一个执行。(只开启一个新的线程,一个任务执行完毕后,再执行下一个任务)
  • 并发队列(Concurrent Dispatch Queue):
可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
注意: 并发队列的并发功能只有在异步(dispatch_async)函数下才有效
两者的具体区别如下两图所示:

3、GCD的使用步骤

①创建一个队列(串行队列或并发队列)
②将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

3.1队列的创建方法/获取方法

  • 可以使用dispatch_queue_creat来创建队列,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空,Dispatch Queue的名称推荐使用应用程序ID这种逆序全程域名;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CURRENT表示并发队列。
  • 对于串行队列,GCD提供了的一种特殊的串行队列:主队列(Main Dispatch Queue)
*所有放在主队列中的任务,都会放到主线程中执行。
*可使用 dispatch_get_main_queue()获取主队列。
  • 对于并发队列,GCD默认提供了全局并发队列(Global  Dispatch Queue)
可以使用 dispatch_get_global来获取。需要传入两个参数。第一个参数是队列优先级,一般用 DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用 0即可。

3.2任务的创建方法

GCD提供了同步执行任务的创建方法 dispatch_sync和异步执行任务的创建方法 dispatch_async
组合方式:
1、同步执行 + 并发队列
2、异步执行 + 并发队列
3、同步执行 + 串行队列
4、异步执行 + 串行队列
实际上,还有两种特殊队列:全局并发队列、 主队列。全局并发队列可以作为普通并发队列来使用。但是主队列因为有点特殊,所以我们就有多了两种组合方式。
5、同步执行 + 主队列
6、异步执行 + 主队列
那么这几种不同组合方式各有什么区别呢?

4、GCD的基本使用

4.1同步执行 + 并发队列

  • 在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
同步执行 + 并发队列 中可以看到:
  • 所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行 不具备开启新线程的能力)。
  • 同步任务需要等待队列的任务执行结束
  • 任务按顺序执行的,按顺序执行的原因:虽然 并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建多线程,只有当前这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时执行。

4.2 异步执行 + 并发队列

  • 可以开启多个线程,任务交替(同时)执行。
在 异步执行 +  并发队列 中可以看出:
  • 除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行 具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。
  • 所有任务是在打印的syncConcurrent---beginsyncConcurrent---end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)

4.3 同步执行 + 串行队列

  • 不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。
在同步执行 + 串行队列可以看到:
  • 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)。
  • 所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。
  • 任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

4.4异步执行 +  串行队列

会开启新线程,但是因为任务是串行的,执行完一个任务,在执行下一个任务
在异步执行 + 串行队列可以看到:
  • 开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。
  • 所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
  • 任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
下面讲讲特殊队列: 主队列。
  • 主队列:GCD自带的一种特殊的串行队列
所有放在主队列的任务,都会放在主线程中执行。
可使用 dispatch_get_main_queue() 获取主队列。
我们再来看看主队列的两种组合方式。

4.5 同步执行 + 主队列

同步执行 + 主队列 在不同线程中调用结果也是不一样的,在主线程中调用会出现死锁,而在其他线程中则不会。
4.5.1 在主线程中调用 同步执行+主队列
  • 互相等待卡住不可行
在 同步执行 + 主队列 可以惊奇的发现:
  • 在主线程中使用 同步执行 + 主队列,追加到主线程的任务1、任务2、任务3不再执行了,而且,syncMain——end也没有打印,在xcode 9 上还会报崩溃。这是为什么呢?
这是因为我们在主线程中执行syncMain方法,相对于把syncMain任务添加到主队列中。而同步执行,会等待当前队列的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程完成处理syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。
那么,现在情况就是syncMain任务和任务1都在等待对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务也执行不了,而且syncMain——end也没有打印。
要是如果不在主线程中调用,而在其他线程中调用会如何呢?
4.5.2在其他线程中调用 同步执行 + 主队列
  • 不会开启新线程,执行完一个任务,在执行下一个任务
在其他线程中使用 同步执行 + 主队列 可看到:
  • 所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有的主队列任务,都会放在主线程中执行)。
  • 所有任务都在打印的syncConcurrent---beginsyncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。
  • 任务是按顺序执行的(主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。
为什么现在就不会卡住了呢?
因为syncMain 任务放到了其他线程里面,而任务1、任务2、任务3都追加到主队列中,这三个任务都会在主线程中执行。syncMain 任务在其他线程中执行到追加任务1到主队列中,因为主队列现在没有正在执行的任务,所以,会直接执行主队列的任务1,等任务1执行完毕,再接着执行任务2、任务3。所以这里才不会卡住线程。

4.6 异步执行 + 主队列

  • 只在主线程中执行任务,执行完一个任务,再执行下一个任务。
在 异步执行 + 主队列 可以看到:
  • 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行 具备开启新线程的能力,但因为是主队列,所以所有任务都在主线程中)。
  • 所有任务都是在打印syncCncurren——begin和syncConcurren——end 之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
  • 任务是按顺序执行的(因为主队列是 串行队列,每次只有一个任务呗执行,任务一个接一个按顺序)。
弄懂了难理解、绕来绕去的 队列 + 任务 之后,我们来学习一个简单的东西:5.GCD线程间的通信。

5.GCD线程间的通信

在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、问价上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么久用到了线程之间的通讯。
可以看到在其他线程中先执行任务,执行完了之后回到主线程执行主线程的相应操作。

GCD 线程间通信方法

GCD 本身不提供直接的线程间通信机制,但我们可以利用一些技巧和工具来实现线程间的有效通信:

1. 主队列与全局队列结合:

  • 在后台线程执行耗时操作,完成后在主队列更新 UI。

  • 使用 DispatchQueue.main.async 将更新 UI 的代码块提交到主队列执行。

DispatchQueue.global().async {
    // 在后台线程执行耗时操作
    ...
    
    DispatchQueue.main.async {
        // 更新 UI 界面
    }
}

content_copyUse code with caution.Swift

2. 使用 DispatchGroup:

  • 将多个异步任务添加到 DispatchGroup 中,等待所有任务完成。

  • 使用 notify 方法在所有任务完成时执行回调函数。

let group = DispatchGroup()

group.enter()
DispatchQueue.global().async {
    // 任务 1
    ...
    group.leave()
}

group.enter()
DispatchQueue.global().async {
    // 任务 2
    ...
    group.leave()
}

group.notify(queue: .main) {
    // 所有任务完成后执行
}

content_copyUse code with caution.Swift

3. 使用 DispatchSemaphore:

  • 利用信号量来控制线程之间的同步和互斥。

  • 一个线程可以等待信号量,另一个线程可以发送信号量,从而实现线程间的协作。

let semaphore = DispatchSemaphore(value: 0)

DispatchQueue.global().async {
    // 执行任务
    ...
    semaphore.signal() // 发送信号
}

semaphore.wait() // 等待信号
// 继续执行后续操作

4. 使用共享内存:

  • 在多个线程之间共享数据,可以使用原子操作 (atomic operations) 或线程安全的集合类 (例如 ConcurrentDictionary) 来保证数据的一致性。

5. 使用第三方库:

  • 可以使用 PromiseKit、RxSwift 等第三方库来简化异步编程和线程间通信。

注意事项:

  • 避免数据竞争:使用同步机制或线程安全的集合类来保护共享数据。

  • 避免死锁:确保线程获取锁的顺序一致,或使用超时机制。

  • 主线程更新 UI:所有 UI 更新操作必须在主线程执行。

总结:

GCD 可以与其他机制结合,实现线程间的有效通信,例如主队列与全局队列结合、DispatchGroup、DispatchSemaphore、共享内存和第三方库等。 开发者需要根据具体需求选择合适的方法,并注意线程安全和性能优化。

6.GCD的其他方式

6.1 GCD栅栏方法:dispatch_barrier_async

  • 我们有时候需要异步执行两组操作,而且第一组操作执行完之后,才能执行第二组操作。这样我们就需要一个相对于栅栏一样的一个方法将两组异步执行的操作给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。dispatch_barrier_async函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在dispatch_barrier_async函数追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列中并开始执行。具体如下图所示:
dispatch_barrier_async.png
在dispatch_barrier_async 执行结果中可以看出:
  • 在执行完栅栏前面你的操作之后,才可以执行栅栏操作,最后再执行栅栏后边的操作。

6.2 GCD延时执行方法:dispatch_after

我们经常会遇到这样的需求:在指定时间(例如3秒)之后执行某个任务。可以用GCD的dispatch_after函数实现。
需要注意的是:dispatch_after函数不是在指定的时间后开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。
可以看出:在打印  asyncMain---begin  之后大约 2.0 秒的时间,打印了  after---<NSThread: 0x60000006ee00>
{number = 1, name = main}

6.3 GCD一次性代码 (只执行一次):dispatch_once 

  • 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once 函数。使用dispatch_once 函数能保证某段代码在程序执行过程中只被执行1次,并且即使在使用多线程的环境下,dispatch_once  也可以保证线程安全。

6.4 GCD 快速迭代方法:dispatch_apply

  • 通常我们会用for循环遍历,但是 GCD 给我们提供了快速迭代的函数 dispatch_apply 。 dispatch_apply 按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。
如果是在串行队列中使用 dispatch_apply,那么就和for循环一样,按顺序同步执行。可这样就提现不出快速迭代的意义了。我们可以利用并发队列进行异步执行。比如遍历0~5这6个数字,for循环的做法是每次取出一个元素,逐个遍历。dispatch_apply 可以在多个线程中同时(异步)遍历多个数字。
还有一点,无论是在串行队列,还是异步队列中,dispatch_apply 都会等待全部任务执行完毕,这点就像是同步操作,也像是队列组中的dispatch_group_wait 方法。
因为是在并发队列中异步执行任务,所以各个任务的执行时间长短不定,最后结束顺序也不定。但是 apply——end 一定在最后执行。这是因为 dispatch_apply 函数会等待全部任务执行完毕。

6.5 GCD队列组:dispatch_group

有时候我们会有这样的需求:分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到GCD的队列组。( 直接异步不行吗?
  • 调用队列组的dispatch_group_async 先把任务放到队列中,然后将队列放入队列组。或者使用队列组的 dispatch_group_enter、dispatch_group_leave 组合来实现 dispatch_group_async。
  • 调用队列组的dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行(会阻塞当前线程)。
6.5.1 dispatch_group_notify
  • 监听group 中任务的完成情况,当所有的任务都执行完,追加任务到group 中,并执行任务。
从dispatch_group_notify相关代码运行输出结果可以看出:
当所有任务都执行完成之后,才执行dispatch_group_notify block中的任务。
6.5.2 dispatch_group_wait
  • 暂停当前线程(阻塞当前线程),等待指定的group中的任务执行完成后,才会往下继续执行。
从 dispatch_group_wait 相关代码运行输出结果可以看出:
当所有任务执行完之后,才执行 dispatch_group_wait 之后的操作。但是,使用 dispatch_group_wait 会阻塞当前线程。
6.5.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中的任务。
从dispatch_group_enter 、dispatch_group_leave 相关代码运行结果可以看出:当前所有任务执行完成之后,才执行dispatch_group_notify 中的任务。这里的 dispatch_group_enter 、dispatch_group_leave 组合,其实等同于 dispatch_group_async。

6.6 GCD信号量:dispatch_semaphore

GCD中的信号量是指 dispatch_semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在Dispatch Semaphore 中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。
  • dispatch_semaphore_creat:创建一个semaphore并初始化信号的总量
  • dispatch_semaphore_signal:发送一个信号,让信号总量加1
  • dispatch_semapore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程,否则就可以正常执行。
dispatch semaphore 在实际开发中主要用于:
  • 保持线程同步,将异步执行任务转换为同步执行任务
  • 保证线程安全,为线程加锁
6.6.1 dispatch semaphore 线程同步
我们在开发中,会遇到这样的需求:异步执行耗时任务,并使用异步执行的结果进行一些额外的操作。换句话说,相当于,将异步执行任务转换为同步执行任务。比如说:AFNetworking 中 AFURLSessionManager.m 里面的taskForKeyPath:方法。通过引入信号量的方式,等待异步执行任务结果,获取到task,然后再返回该tasks。
下面,我们利用dispatch semaphore 实现线程同步,将异步执行任务转换为同步执行任务。
从dispatch semaphore 实现线程同步的代码可以看到:
  • semaphore——end 是在执行完 number = 100;之后才打印的。而且输出结果number 为100.
这是因为 异步执行 不会做任何等待,可以继续执行任务。异步执行任务1追加到队列之后,不做等待,接着执行 dispatch_semaphore_wait 方法。此时semaphore == 0 ,当前线程进入等待状态。然后,异步任务1开始执行。任务1执行到 dispatch_semaphore_signal 之后,总信号量,此时semaphore = = 1,dispatch_semaphore_wait 方法使总信号量减1,正在被阻塞的线程(主线程)恢复继续执行。最后打印semaphore——end,num = 100。这样就实现了线程同步,将异步执行任务转换为同步执行任务。
6.2.2 dispatch Semaphore 线程安全和线程同步 (为线程枷锁)
线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线运行的结果是一样的,而且其他的变量的值业界预期的是一样的,就是线程安全的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作(更改变量),一般都需要考虑线程同步,否则的话可能影响线程安全。
线程同步:可理解为线程A和线程B一块配合,A执行到一点程度时要依靠线程B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
举个简单例子就是:两个人一起来聊天。两人不能同时说话避免听不清(操作冲突)。等一个人说完(一个线程结束操作),另一个再说(另一个线程再开始执行操作)。
下面,我们模拟火车票售卖的方式,实现NSThread 线程安全和解决线程同步问题。
场景:总共50张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。
6.6.2.1 非线程安全 (不使用 semaphore)
先来看看不考虑线程安全的代码:
可以看到在不考虑线程安全的情况下,不使用semaphore 的情况下,得到票数数错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。
6.6.2.2 线程安全 (使用 semaphore加锁)
考虑线程安全的代码:
可以看出,在考虑线程安全的情况下,使用dispatch_semaphore 机制之后,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。

7. Swift 中的多线程技术:并发编程的利器

Swift 主要通过 Grand Central Dispatch (GCD) 和 Operation Queues 实现多线程编程。 以下介绍这两种技术的使用方法:

1. Grand Central Dispatch (GCD):

  • 创建队列:

// 串行队列

let serialQueue = DispatchQueue(label: "com.example.serialQueue")

// 并发队列

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

// 主队列 (串行)

let mainQueue = DispatchQueue.main

content_copy

Use code with caution.

Swift

  • 提交任务:

// 异步执行任务

concurrentQueue.async {

    // 在后台线程执行的任务

    print("Task running in background thread")

}

// 同步执行任务

serialQueue.sync {

    // 在当前线程执行的任务

    print("Task running in current thread")

}

// 在主线程执行任务

DispatchQueue.main.async {

    // 更新 UI 等操作

    print("Task running in main thread")

}

content_copy

Use code with caution.

Swift

2. Operation Queues:

  • 创建队列:

let operationQueue = OperationQueue()

content_copy

Use code with caution.

Swift

  • 创建操作:

let operation = BlockOperation {

    // 要执行的任务

    print("Operation running")

}

content_copy

Use code with caution.

Swift

  • 添加操作到队列:

operationQueue.addOperation(operation)

content_copy

Use code with caution.

Swift

  • 设置依赖关系:

let operation2 = BlockOperation {

    // ...

}

operation2.addDependency(operation) // operation2 依赖于 operation

operationQueue.addOperations([operation, operation2], waitUntilFinished: false)

content_copy

Use code with caution.

Swift

区别和选择:

  • GCD: 更轻量级,适用于简单的并发任务。
  • Operation Queues: 更高级,可以管理任务之间的依赖关系,支持取消、暂停、恢复任务,以及 KVO 观察任务状态,适用于更复杂的并发场景。

注意事项:

  • 线程安全: 访问共享资源时,需要注意线程安全问题,避免数据竞争。 可以使用 DispatchQueue 的同步方法或互斥锁等机制来保证线程安全。
  • 主线程更新 UI: 更新 UI 操作必须在主线程执行,可以使用 DispatchQueue.main.async 将任务提交到主线程。

总结:

Swift 提供了 GCD 和 Operation Queues 两种多线程技术,开发者可以根据具体需求选择合适的技术,并注意线程安全和性能优化。

8. iOS 多线程技术:并发编程的选项和区别

iOS 开发中,多线程技术可以提升应用的性能和响应速度,改善用户体验。 主要有以下几种常见的多线程技术,它们之间存在一些区别:

1. Grand Central Dispatch (GCD):

  • 特点: 底层 C 语言 API,轻量级,高效。
  • 使用方式: 创建队列 (串行或并发),将任务 (block) 提交到队列中执行。
  • 优点: 简单易用,性能优越,由系统管理线程生命周期。
  • 缺点: 控制粒度较粗,无法直接控制线程生命周期和状态。

2. Operation Queues:

  • 特点: 基于 GCD 构建的面向对象 API,更高级别的抽象。
  • 使用方式: 创建 Operation 对象封装任务,设置依赖关系,将其添加到队列中执行。
  • 优点: 易于管理任务之间的依赖关系,支持取消、暂停、恢复任务,可以 KVO 观察任务状态。
  • 缺点: 比 GCD 复杂一些,性能可能略低于 GCD。

3. Threads:

  • 特点: 最底层的线程 API,提供对线程的完全控制。
  • 使用方式: 创建 Thread 对象,实现线程执行函数,启动线程。
  • 优点: 完全控制线程生命周期和行为,可以进行更细粒度的控制。
  • 缺点: 使用复杂,需要手动管理线程同步和资源竞争问题,容易出错。

4. NSThread (Objective-C):

  • 特点: Objective-C 中用于创建和管理线程的类,功能类似于 Swift 中的 Thread 类。
  • 使用方式: 创建 NSThread 对象,实现线程执行方法,启动线程。
  • 注意: 在 Swift 中,通常使用 GCD 或 Operation Queues,而不是 NSThread。

选择哪种多线程技术:

  • 简单并发任务: GCD 是首选,简单易用且高效。
  • 复杂任务管理,需要依赖关系和状态控制: Operation Queues 更合适。
  • 需要完全控制线程: 使用 Threads,但需注意线程安全问题。

区别总结:

技术

特点

优点

缺点

GCD

轻量级,高效

简单易用,性能优越

控制粒度粗,无法直接控制线程

Operation Queues

面向对象,更高级抽象

易于管理任务,支持更多控制选项

比 GCD 复杂,性能略低

Threads

底层,完全控制

精确控制线程生命周期和行为

使用复杂,易出错

无论使用哪种技术,都需要注意线程安全和资源竞争问题,确保代码的正确性和稳定性。


 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值