iOS iOS中的多线程入门

随着我们所有移动设备中多核处理芯片的出现,它带来了在应用程序中同时执行多个任务的可能性。 在iOS中, 为了使应用程序的UI保持响应状态,所有繁重的任务都添加到了新线程中,例如,网络调用。 这些线程通常在后台运行,并在必要时发送回叫以更新UI。

苹果文档中已完美说明了多线程需求,它说:

OS X或iOS中的每个进程(应用程序)都由一个或多个线程组成,每个线程代表通过应用程序代码执行的单个路径。 每个应用程序都从一个线程开始,该线程运行该应用程序的主要功能。 应用程序可以产生其他线程,每个线程都执行特定功能的代码。

为了方便和安全地管理这些额外的线程,我们需要一个框架,例如dispatch

multi 实现多线程的方法:

–大中央派遣

– NSThread

– NSOperationQueue

本博客假定您对这些类型有基本的了解。

比较这些方法时:

Code/Structural Origins:

  • GCD是基于C的底层API。
  • NSOperation和NSOperationQueue是Objective-C类(GCD上的包装器)。
  • NSThread是一个NSObject(使用pthreads)。

Complexity comparison:

  • 对于GCD实施非常轻巧
  • NSOperationQueue非常复杂且重量级
  • NSThread只是pthread的包装器,因此也使其轻巧。

NSOperation advantages over GCD and NSThread:
您可以:

  • 因此,在两个NSOperation之间建立依赖关系,使开发人员可以按特定顺序执行任务。
  • 一旦任务开始执行,就暂停,取消,恢复NSOperation,因此可以控制该操作的生命周期。
  • 监视操作的状态,例如:准备,执行或完成。
  • 指定可以同时运行的最大排队操作数。

显然,NSOperationQueue为您提供了对操作的更多控制。

一旦确定了要使用的方法,就需要确定其顺序和优先级。

Order of Operation:
队列可以有两种类型:串行队列或并发队列,分别分别同步和异步运行任务

Priority of operations:
优先级定义为称为QoS的服务质量,分为以下四种类型:

现在,我们通过预测几个示例的输出来尝试了解这些操作。
在尝试回答下面的代码输出之前,让我们看一下dispatch_async和dispatch_sync的文档:

Dispatch_async:

🤞🏼声明void dispatch_async(dispatch_queue_t队列,dispatch_block_t块)👥讨论此函数是用于将块提交到调度队列的基本机制。 提交该块后,始终会立即返回对此函数的调用,并且永远不要等待该块被调用。 目标队列确定相对于提交给同一队列的其他块是串行调用还是并行调用该块。 独立的串行队列相对于彼此并发处理。 Summary‍♀️摘要

提交一个块以在调度队列上异步执行并立即返回。

Dispatch_sync:

🤞🏼声明void dispatch_sync(dispatch_queue_t queue,dispatch_block_t block); 👥讨论将块提交到调度队列以进行同步执行。 与dispatch_async不同,该函数直到块完成才返回。 调用此函数并以当前队列为目标会导致死锁。 与dispatch_async不同,对目标队列不执行任何保留。 因为对该函数的调用是同步的,所以它“借用”了调用者的引用。 此外,不对该块执行Block_copy。 作为一种优化,此函数在可能的情况下在当前线程上调用该块。 Summary‍♀️摘要

提交一个块对象以在调度队列上执行,然后等待该块完成。

现在尝试这些:

这些日志将以什么顺序打印? 建议您在查看解决方案之前先尝试一下。

给我们什么?
从全局队列(这是并发队列)创建一个新的“队列”。 我们给了2个调度任务,很少打印NSLog来了解流程。

因此输出将是:

正如我们期望的那样,首先打印异步块之后的字符串,然后是该块下的任务。 原因在文档中已明确说明,异步任务在提交后立即返回。

同意我吗 但…

有一个转折:🔀
由于我们已经从全局队列中创建了一个并发队列并将其添加到异步调度中,因此请确保它在提交后立即返回,但是创建异步调度的关键是允许多个线程同时执行。 因此,这里的主队列(打印“数量%num”的任务)和异步调度队列中的任务可以同时运行。 因此,您也可以按以下顺序期望输出:

有道理?

类似地,现在预测输出,如果在第一个示例中我们使用dispatch sync代替了dispatch_async。 在查看解决方案之前,请再次尝试自己回答。

如文档所述:“直到该块完成,该函数才会返回”。在sync_sync块中,所有事情以最平静的方式发生。 :blush:打印“ 1 01 001”,然后出现另一个dispatch_sync,外部的将不会继续执行第46行,直到执行了第41至44行。

但是假设内部队列是异步的,那么它将立即返回并且在45和40之后的行将同时执行。

最后一个变量是两个队列都为dispatch_async时。 😃

我确定您一定已经猜到了输出。

这将全部并发。

恭喜您,您已经了解了多线程的本质。 如果您对这些示例充满信心,我相信您现在可以在应用程序中使用线程。

另外,您必须尝试创建自己的新队列。

句法:

串行队列:
dispatch_queue_t serial_queue = dispatch_queue_create("your.queue.label", DISPATCH_QUEUE_SERIAL);

并发队列:
dispatch_queue_t concurrent_queue = dispatch_queue_create("your.queue.label", DISPATCH_QUEUE_CONCURRENT);

死锁
您应该始终避免将同一队列(尤其是串行队列)传递给多个调度块,因为这可能导致死锁。

例如。

该代码导致死锁条件,因为外部异步操作等待内部块启动和完成,而内部块直到“队列”中的任务完成才开始。

与并发队列一起正常工作的地方:

只是要强调,这是可行的,但最好不要使用相同的队列

最后,一个很好的死锁示例:

据称这是创建死锁的最短代码。 我希望到现在为止,您必须能够思考为什么会造成僵局。

Hint:主队列是一个串行队列,此代码试图在运行并在dispatch_sync上等待的同一队列上同步调度新代码块。

如果这一切让人觉得不知所措,那就可以了。 您尝试的示例越多,就越容易理解。 如果您想更详细地学习这些主题,请告诉我。 您可以在我的github上找到该项目。

在下一个博客中,我们将研究锁和最著名的读者/作家问题😊感谢您的阅读。 👓