iOS 并行编程:Thread

1 创建线程

1.1 NSThread

      使用 NSThread 来创建线程有两个可以使用的方法:

1) 使用 detachNewThreadSelector:toTarget:withObject:类方法来生成一个新的线程

2) 创建一个新的 NSThread 对象,并调用它的 start 方法。

这两种创建线程的技术都在你的应用程序里面新建了一个脱离的线程。 一个脱离的线程意味着当线程退出的时候线程的资源由系统自动回收。

1 -(void) myThreadMainMethod
 2 {
 3     printf("myThreadMainMethod\n");
 4 }
 5 
 6 -(void) testMethod
 7 {
 8     NSThread* myThread = [[NSThread alloc] initWithTarget: self
 9                                                  selector:@selector(myThreadMainMethod)
10                                                    object:nil];
11     [myThread start];
12 }

如果你拥有一个 NSThread 对象,它的线程当前真正运行,你可以给该线程发送消息的唯一方法是在你应用程序里面的任何对象使用performSelector:onThread:withObject:waitUntilDone:方法。

1.2 POSIX

Mac OS X 和 iOS 提供基于 C 语言支持的使用 POSIX 线程 API 来创建线程的方法。

1 void* PosixThreadMainRoutine(void* data) //这是C语言的普通函数
 2 {
 3     printf("PosixThreadMainRoutine\n");
 4     return NULL;
 5 }
 6 -(void) createPOSIXThreads //这是Object-C类的成员方法
 7 {
 8     pthread_t posixThreadID;
 9     int threadError = pthread_create(&posixThreadID, NULL, &PosixThreadMainRoutine, NULL);
10 }

1.3 NSObject

在 iOS 和 Mac OS X v10.5 及其之后,所有的对象都可能生成一个新的线程,并用它来执行它任意的方法。方法 performSelectorInBackground:withObject:新生成一个脱离的线程,使用指定的方法作为新线程的主体入口点。

比如,你可以使用当前对象创建一个新的线程:

1 -(void) myThreadMainMethod
2 {
3     printf("myThreadMainMethod\n");
4 }

6 -(void) createNSObjectThreads
7 {
8     [self performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil];
9 }

调用该方法的效果和你在当前对象里面使用NSThread 的detachNewThreadSelector:toTarget:withObject:传递 selectore,object 作为参数的方法一样。新的线程将会被立即生成并运行,它使用默认的设置。

2 run loop

Run loop 是线程相关的的基础框架的一部分。一个 run loop 就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用 run loop 的目的是让你的线程在有工作的时候忙于工作, 而没工作的时候处于休眠状态。Run loop相当是Linux系统中的信号处理程序,需要绑定感兴趣的信号。

Run loop 的管理并不完全自动的。你仍然需要设计你的线程代码在合适的时候启动 run loop 并正确响应输入事件。 Cocoa 和 Core Fundation 都自动在你应用程序的主线程启动一个 run loop objects 来帮助配置和管理你线程的 run loop。你的应用程序不需要显式的创建这些对象(run loop objects);每个线程,包括程序的主线程都有与之对应的 run loop object。 只有辅助线程才需要显式的运行它的 run loop。

2.1 剖析

Run loop是用来响应产生的事件,在代码中自己实现循环控制语句,来响应接收到的事件并调用处理程序。Run loop 接收输入事件来自两种不同的来源:

  • 输入源(input source):从其它线程或应用传递来的异步事件。
  • 定时源 (timer source):是一种同步事件,发生在特定时间或者重复的时间间隔。

如图 1所示是run loop与source之间的结构,run loop在接收到input source事件后,就会导致runUntilDate方法退出;而接收到timer source事件后,则不会退出。其中run loop除了能接收source事件外,还能产生notifications(消息),并可以指定observers来接收这些消息。

图 1

2.1.1 模式

run loop 有如下的几种模式,每种模式都是用来监听input source、timer source和notifications事件。每次在运行run loop时都需要为run loop指定某种模式:

2.1.2 第一个程序:定时源

如下是一个简单的定时器功能,其中在0.1秒后将触发响应方法doFireTimer 。

1 -(void) doFireTimer //定时器触发方法
 2 {
 3     printf("doFireTimer\n");
 4 }
 5 
 6 -(void) myThreadMainMethod //线程的入口函数
 7 {
 8     printf("myThreadMainMethod\n");
 9 
10     NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; //创建一个run loop对象
11     NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:0.1];//设置run loop持续运行的时间
12     NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate //创建一个定时器对象
13                                                 interval:0.1
14                                                   target:self
15                                                 selector:@selector(doFireTimer)
16                                                 userInfo:nil                                                  repeats:YES]; 
17     [myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode]; //将定时器对象添加到run loop对象中。
18     [myRunLoop runUntilDate:futureDate];  //启动run loop对象。
19 }

2.2 使用

2.2.1 获得

为了获得当前线程的 run loop,你可以采用以下任一方式:

1) 使用 NSRunLoop 的 currentRunLoop 类方法来返回一个NSRunLoop 对象;

2) 使用CFRunLoopGetCurrent函数返回一个CFRunLoopRef对象。

如:

1 NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];   //方式1
2 CFRunLoopRef runLoop = CFRunLoopGetCurrent();         //方式2

CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。NSRunLoop 类定义了一个 getCFRunLoop 方法,该方法返回 一个可以传递给 Core Foundation 例程的 CFRunLoopRef 类型。因为两者都指向同一个run loop。

2.2.2 启动

run loop有三种启动方式,不同的启动方式调用不同的方法,包括以下这些:

表 3


方式


成员函数


描述


无条件


(void)run


最简单的方法,但也最不推荐使用的,可以添加或删除输入源和定时器,但是退出 run loop 的唯一方法是杀死它。没有任何办法可以让这 run loop 运行在自定义模式下。


有限时间


(void)runUntilDate:(NSDate *)limitDate


这种方式一直运行直到到某一事件到达或者规定的时间已经到期。如果是事件到达消息会被传递给相应的处理程序来处理,然后run loop 退出。你可以重新启动 run loop 来等待下一事件。如果是规定时间到期了,你只需简单的重启 run loop 或使用此段时间来做任何的其他工作。


特定模式


(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate


这种方式是前两种方式的结合体。

2.2.3 退出

有两种方法可以让 run loop 处理事件之前退出:

1) 给 run loop 设置超时时间

2) 通知 run loop 停止

2.2.4 时机

仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个 run loop。如果你使用 xcode提供的模板创建的程序,那么永远不需要自己显式的调用这些例程。Run loop 在要和线程有更多的交互时才需要,比如以下情况:

  • 使用端口或自定义输入源来和其他线程通信
  • 使用线程的定时器
  • Cocoa中使用任何performSelector...的方法
  • 使线程周期性工作

2.3 配置

在你在辅助线程运行 run loop 之前,你必须至少添加一输入源或定时器给它。 如果 run loop 没有任何源需要监视的话,它会在你启动之际立马退出。

2.3.1 定时源

      为了给Run Loop配置一个定时源,只需创建一个定时器(NSTimer)对象并把它添加到NSRunLoop对象中。 其中定时源也有指针类型:CFRunLoopTimerRef,该类型是配合run loop的指针类型CFRunLoopRef使用的。并且将NSTimer 对象添加到NSRunLoop对象中有两种方式:自动和手动:

1) 自动

这种方式是通过调用NSTimer 的静态方法scheduledTimerWithTimeInterval,从而自动将创建的NSTimer 对象添加到run loop中。

2) 手动

这种方式是手动创建NSTimer 对象,然后调用NSRunLoop对象的addTimer方法将其添加进入run loop中。

如:

1 NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
 2 NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
 3 
 4 //自动方式,自动将NSTimer 对象添加到myRunLoop 中。
 5 [NSTimer scheduledTimerWithTimeInterval:0.2
 6             target:self
 7             selector:@selector(myDoFireTimer1:) //这里的myDoFireTimer1方法是当时间到了后,响应的成员方法。
 8             userInfo:nil
 9             repeats:YES];
10 
11 //手动方式,需要手动调用addTimer方法将myTimer添加到myRunLoop 中
12 NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate
13                           interval:0.1
14                           target:self
15                           selector:@selector(myDoFireTimer2:)
16                           userInfo:nil
17                           repeats:YES];
18 [myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];

2.3.2 观察者

除了安装源,你也可以添加 run loop 观察者来监视 run loop 的不同执行阶段情况。为了给 run loop 添加一个观察者,你可以创建 CFRunLoopObserverRef 不透明类型,并使用 CFRunLoopAddObserver 将它添加到你的 run loop。Run loop 观察者必须由 Core foundation 函数创建,即使是 Cocoa 程序。

1 -(void) myThreadMainMethod
 2 {
 3     printf("myThreadMainMethod\n");
 4     
 5     NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
 6     CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
 7     CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
 8          if (observer)     {         CFRunLoopRef    cfLoop = [myRunLoop getCFRunLoop];         CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);     }
 9     
10     NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:0.1];
11     NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate
12                                                 interval:0.1
13                                                   target:self
14                                                 selector:@selector(doFireTimer)
15                                                 userInfo:nil                                                  repeats:YES];     [myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];
16     [myRunLoop runUntilDate:futureDate];    
17 }

2.3.2 输入源

对于Apple提供的guide中有关自定义输入源和port输入源真心看不懂,为了内容的完整性,所以将此部分添加在此,若有谁清楚这块内容的,希望能介绍介绍。

3 参考文献

[1] Threading Programming Guide.

时间: 2024-10-17 12:34:14

iOS 并行编程:Thread的相关文章

iOS 并行编程:Operation Queues

1 简介 1.1 功能        Operation Queue也是IOS的一种并行编程技术,类似Dispatch Queue可以帮助用户管理多线程.但是Operation Queue将任务封装在NSOperation对象中,从而可以更好的控制任务的执行.并且Dispatch Queue的先入先出的执行方式不同,Operation Queue任务的执行顺序可以控制.其中IOS是将任务交给NSOperation对象进行管理,其中NSOperation是个抽象类,必须被继承,目前系统预定义了两个

iOS 并行编程:GCD Dispatch Queues

1 简介 1.1 功能       Grand Central Dispatch(GCD)技术让任务并行排队执行,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务.任务可以是一个函数(function)或者是一个block. GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节. GCD中的队列称为dispatch queue,它可以保证先进来的任务先得到执行通过它能够大大简化多线程编程.工程师只要将要执行的任务(执行代码块)放入队列中,GCD将会为需要执行的任务创建

iOS 并行编程:GCD Dispatch Sources

1 简介 dispatch source是一种用于处理事件的数据类型,这些被处理的事件为操作系统中的底层级别.Grand Central Dispatch(GCD)支持如下的dispatch sources类型: Timer dispatch sources:定时器类型,能够产生周期性的通知事件: Signal dispatch sources:信号类型,当UNIX信号到底时,能够通知应用程序: Descriptor sources:文件描述符类型,处理UNIX的文件或socket描述符,如:

iOS多线程编程

1. 进程,线程, 任务 进程:一个程序在运行时,系统会为其分配一个进程,用以管理他的一些资源. 线程:进程内所包含的一个或多个执行单元称为线程,线程一般情况下不持有资源,但可以使用其所在进程的资源. 任务:进程或线程中要做的事情. 在引入线程的操作系统中,通常把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位. 线程比进程更小,对其调度的开销小,能够提高系统内多个任务的并发执行程度. 一个程序至少有一个进程,一个进程至少有一个线程.一个程序就是一个进程,而一个程序中的多个任

iOS并发编程对比总结,NSThread,NSOperation,GCD - iOS

1. 多线程概念 进程 正在进行中的程序被称为进程,负责程序运行的内存分配 每一个进程都有自己独立的虚拟内存空间 线程 线程是进程中一个独立的执行路径(控制单元) 一个进程中至少包含一条线程,即主线程 可以将耗时的执行路径(如:网络请求)放在其他线程中执行 创建线程的目的就是为了开启一条新的执行路径,运行指定的代码,与主线程中的代码实现同时运行 1.1 多任务系统调度示意图 说明:每个应用程序由操作系统分配的短暂的时间片(Timeslice)轮流使用CPU,由于CPU对每个时间片的处理速度非常快

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等 线程 使用Instruments的CPU strategy view查看代码如何在多核CPU中执行.创建线程可以使用POSIX 线程API,或者NSThread(封装POSIX 线程API).下面是并发4个线程在一百万个数字中找最小值和最大值的pthread例子: #import

C#并行编程 z

目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 C#并行编程-线程同步原语 C#并行编程-PLINQ:声明式数据并行 背景 基于任务的程序设计.命令式数据并行和任务并行都要求能够支持并发更新的数组.列表和集合. 在.NET Framework 4 以前,为了让共享的数组.列表和集合能够被多个线程更新,需要添加复杂的代码来同步这些更新操作. 如您需要编写一个并行循环,这个循环以无序的方式向一个共享集合中添加元素,那么必须加入一个同步机制

.Net中的并行编程-4.实现高性能异步队列

上文<.Net中的并行编程-3.ConcurrentQueue实现与分析>分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队列主要用于实时数据流的处理并简化多线程编程模型.设计该队列时考虑以下几点需求(需求来自公司的一个实际项目): 1. 支持多线程入队出队,尽量简化多线程编程的复杂度. 2. 支持事件触发机制,数据入队时才进行处理而不是使用定时处理机制, 而且内部能阻塞消费者线程. 3. 出队时数据处理的顺序要保证和入队时是一致

Java:并行编程及同步使用方法

知道java可以使用java.util.concurrent包下的 CountDownLatch ExecutorService Future Callable 实现并行编程,并在并行线程同步时,用起来十分简单的一种 .实现原理: 1.CountDownLatch 统计并行线程完成数,并提供了await()方法,实现等待所有并行线程完成,或者指定最大等待时间.2.ExecutorService提供了execute(Callable)执行线程方法,还提供了submit(Callable)提交线程.