如果对线程了解不够清楚,在项目就使用线程,会给开发带来很多问题。所以在iphone的项目中使用线程,最好先学习Apple的线程开发向导。以下是自己开发过程中,使用线程遇到的问题,以及解决的方法。 第一个问题,为什么要使用线程。在解决这个问题之前,要先了解什么是线程。在官方的线程开发文档中有明确的定义:
Threads are a relatively lightweight way to implement multiple paths of execution inside of an application.( 线程是一个相对轻量级的,在应用程序内部,实现执行的多条路径的方式。)
通俗的讲就是, 程序中的函数调用是一步一步执行的,而线程可以让多个函数同时调用,根据内核程序的调度,在不同的时间执行不同的函数,结果就像是程序在执行的过程中同时开通了多条路径,而如果没有线程,程序只能走一条路径。所以线程就自然有下面的两个优点:
-
多线程可以提高应用程序的响应能力,也就是,有了多线程应用程序才能一边在后台处理数据,一边还能接受用户的输入响应。否则只能等数据处理完,才能接受用户的输入响应。
-
在多核系统中,多线程能够提高应用程序的实时行动。因为多线程可以安排程序多条执行路径,所以在多核环境下,每个核同时可以执行不同路径的代码,这样就提高了应用程序的执行能力,同时也缩短了执行时间。
那么已经可以下结论了,之所以使用多线程,无非是2个原因。
-
为了使应用程序在处理数据的同时,不影响用户对应用程序的正常操作,就要使用多线程。否则用户只能等待数据处理完毕,这往往是人们无法忍受的。
-
如果应用程序中有大量的IO中断(譬如文件的读写,网络的访问…),以及用户的大量输入,这样处理器为了等待这些中断,就会处于空闲状态。而多线程恰恰可以利用了这些空闲状态,让应用程序做一些必要的计算,可以大大提升应用程序的执行效率。
网络下载是程序设计中经常遇到的问题,根据前面的介绍,会发现这里就需要用到多线程。因为用户在程序从网络上下载数据的过程,不可能等待数据到达。那么就针对这个实例,来说明第二个问题如何使用线程。
解决第二个问题之前,要引入一个概念Run Loops。可能在做windows及linux线程开发的过程中,没有听过这个概念。所以这里非常有必要解释以下,其实根据官方文档的解释,我们可以完全理解Run loop的用途,以及它和线程的关系。
A run loop is a piece of infrastructure used to manage events arriving asynchronously on a thread.( 一个Run loop是在线程中管理事件异步到达的基础设备)
这里就有一个问题了,什么是异步到达。这里有个例子可以形象的表述这个问题。假如你饿了想吃东西了,你可能会亲自出去一趟,到某个便利店买点东西,然后吃掉。还有一个方法就是,你打电话给某个便利店帮你送一份外卖,这是你只需要在家里等外卖的人,通知你就可以了。第二个方法就是所谓的异步。所以转到计算机程序设计里面,异步就是当主程序中执行某个方法的时候,主程序不需要知道这个方法什么时候执行完,而是这个方法执行完的时候通知主程序就可以了。
根据上面的介绍,你会发现在线程中,常常会有2中不同的方式执行线程中的代码。第一个,把你要执行的所有的代码都写入到线程中,这样自然写入的代码执行完毕,线程同时就结束了。而第二种方式 是你在线程中开启了一个方法,这个方法可能是你开的另外一个线程,你不知道这个方法什么时候执行完,需要这个方法执行完时,通知你的主线程,那么主线程怎么知道什么时候应该结束那?这里就必须要用到run loop。Run loop正如他字面意思一样,就是提供一个循环,就是当有异步输入源的时候,就等待异步输入源的结束。一个Run loop也就是当没有事情要做的时候,它就使线程进入休眠,有事情做的时候就开启线程。所以你完全可以自己写代码按照Run loop的思想,实现线程中的异步输入源的控制。官方API也解释了Run loop的使用要求:
You are not required to use a run loop with any threads you create but doing so can provide a better experience for the use. Run loops make it possible to create long-lived threads that use a minimal amount of resources. Because a run loop puts its thread to sleep when there is nothing to do, it eliminates the need for polling, which wastes CPU cycles and prevents the processor itself from sleeping and saving power.(你不必要求创建的每个线程都使用run loop,但是如果你这样做了,能够给使用者提供一个很好的经历。Run loops使创建一个长期生存的线程而使用很少的资源成为可能性。因为当线程没事可做的时候,一个run loop可以使线程休眠。同时,也取消了线程状态切换的需求,减少了CPU时间片,同时放置处理器休眠,并且节约了能量)
这里你就明白了为何苹果的工程师要为线程提供一个Run loop那,就是为了减少线程在等待异步时间的时候,对资源的消耗。所以如果理解了Run loop,线程如何使用就显而易见了。下面的实例代码就是线程如何处理异步输入源的,以及Run loop在其中的作用:
- (void)asynchronousSource{
for (int i = 0; i < 100; i++) {
NSLog(@"asynchronous input source run");
}
[[NSThread currentThread] cancel];
}
- (void)thread{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]init];
[self performSelector:@selector(asynchronousSource) withObject:nil afterDelay:1];
NSLog(@"me");
BOOL done = NO;
do{
// Start the run loop but return after each source is handled.
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
// If a source explicitly stopped the run loop, or if there are no
// sources or timers, go ahead and exit.
if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
done = YES;
if ([_myThread isCancelled]) {
done = YES;
NSLog(@"thread exit");
}
// Check for any other exit conditions here and set the
// done variable as needed.
}
while (!done);
// while (done) {
// [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
// if ([_myThread isCancelled]) {
// finish = NO;
// NSLog(@"thread exit");
// }
// }
[pool release];
}
这里Run loop是使用官方提供的core function中的函数实现的,而注释掉的代码是使用cocoa中的函数实现。如果要了解这两种实现方法,可以在API文档中找到详细的例子,这里不再赘余。