码迷,mamicode.com
首页 > 编程语言 > 详细

我对多线程的理解和分类

时间:2016-04-26 21:11:56      阅读:308      评论:0      收藏:0      [点我收藏+]

标签:

一、多线程的定义和使用信息:

多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径

在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的。

然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务。

概要提示:

iPhone中的线程应用并不是无节制的,官方给出的资料显示,iPhone OS下的主线程的堆栈大小是1M,第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力

(一)线程概述

有些程序是一条直线,起点到终点——如简单的hello world,运行打印完,它的生命周期便结束了,像是昙花一现。

有些程序是一个圆,不断循环直到将它切断——如操作系统,一直运行直到你关机。

一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。

Mac和IOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程。主线成在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的操作必须在主线程进行。

系统中每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则公用进程的内存空间。

每创建一个新的进成,都需要一些内存(如每个线程有自己的stack空间)和消耗一定的CPU时间。

当多个进成对同一个资源出现争夺的时候需要注意线程安全问题

创建线程

创建一个新的线程就是给进程增加一个执行流,所以新建一个线程需要提供一个函数或者方法作为线程的进口。

(1)使用NSThread

NSThread提供了创建线程的路径,还可以提供了监测当前线程是否是主线程的方法

使用NSThread创建一个新的线程有两种方式:

      1.创建一个NSThread的对象,调用Start方法——使用一个目标对象的方法初始化一个NSThread对象,或者创建一个继承自NSThread的子类,实现起main方法?,然后在直接创建这个子类的对象。

      2.使用detachNewThreadSelector:toTarget:withObject:这个类方法创建一个子线程,这个比较直接,直接使用目标对象的方法作为线程启动入口

(2)使用NSObject

使用NSObject直接就加入了对多线程的支持,允许对象的某个方法在后台运行。

[my0bj performSelectorInBackground:@selector(doSomething) withObject:nil];

(3)POSIX Thread

由于Mac和IOS都是基于Darwin系统,Darwin系统的UNX内核,是基于mach和BSD的,继承了BSD的POSIX接口,所以可以直接使用POSIX线程的相关接口开实现线程

创建线程的接口为 pthread_create, 当然在创建线程之前可以创建好相关线程的属性

——————————————————————————————————————————————————————————————

NSOperation&NSOperationQueue

很多时候我们使用多线程,需要控制线程的并发数,毕竟线程也是需要消耗系统资源的,当程序中同时运行的线程过多时,系统必然变慢,所以很多时候我们会控制同时运行线程的数目

NSOperation可以封装我们的操作,然后将创建好的NSOperation对象放到NSOperationQueue队列中,OperationQueue便开始启动新的线程去执行队列中的操作,OperationQueue的并发数时可以通过如下方式进行设置的:

- (void)setMaxConcurrentOperationCount:(NSInteger)count

GCD时Grand central Dispatch的缩写,是一系列BSD层面的接口。在mac10.6和IOS4.0以后才引入的

且现在NSOperation和NSOperationQueue的多线程的实现就是基于GCD的。目前这个特性也被移植到 FreeBSD上了,可以查看libdispatch这个开源项目。

?
1
dispatch_queue_t imageDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
?
1
当然,GCD除了处理多线程外还有很多非常好的功能,其建立在强大的kqueue之上,效率也能够得到保障。

IOS的多线程,一般分为三种方式:

1、Thread;

2、Cocoa operations;

3、Grand Central Dispatch (GCD) (iOS4 才开始支持)

下面简单说明一下:

1:NSThread

创建方式主要有两种:

[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(myThreadMainMethod:)
object:nil];
[myThread start]; //启动线程

这两种方式的区别是:前一种一调用就会立即创建一个线程来做事情;而后一种虽然你 alloc 了也 init了,但是要直到我们手动调用 start 启动线程时才会真正去创建线程。这种延迟实现思想在很多跟资源相关的地方都有用到。后一种方式我们还可以在启动线程之前,对线程进行配置,比如设置 stack 大小,线程优先级。

此外还有一种间接的方式:利用NSObject的方法

performSelectorInBackground:withObject: 来创建一个线程:
[myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil]; //在后台运行某一个方法
其效果与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。

2、NSOperation:

官方解释:The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform the actual task.

并发执行你需要重载如下4个方法

//执行任务主函数,线程运行的入口函数

-(void)start

//是否允许并发,返回YES,允许并发,返回NO不允许。默认返回NO

-(BOOL)isConcurrent

- (BOOL)isExecuting

//是否已经完成,这个必须要重载,不然放在放在NSOperationQueue里的NSOpertaion不能正常释放。

- (BOOL)isFinished

比如TestNSOperation:NSOperaion 重载上述的4个方法,

声明一个NSOperationQueue,NSOperationQueue *queue = [[[NSOperationQueue alloc ] init]autorelease];

[queue addOperation:testNSoperation];

它会自动调用TestNSOperation里的start函数,如果需要多个NSOperation,你需要设置queue的一些属性,如果多个NSOperation之间有依赖关系,也可以设置,具体可以参考API文档。

非并发执行

-(void)main

只需要重载这个main方法就可以了。

3、GCD

dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.

之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.GCD里就有三种queue来处理。

1. Main queue:

  顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用MainQueue.

2.Serial quque(private dispatch queue)

  每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的.

3. Concurrent queue(globaldispatch queue):

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.

所以我们可以大致了解使用GCD的框架:

1

2

3

4

5

6

7

dispatch_async(getDataQueue,^{

//获取数据,获得一组后,刷新UI.

dispatch_aysnc(mainQueue, ^{

//UI的更新需在主线程中进行

};

}

)

下面就来总结一下这三种多线程方式的区别吧:

Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间, 它们拥有对数据相同的访问权限。你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。在 iOS 中我们可以使用多种形式的 thread:

Cocoa threads: 使用NSThread 或直接从 NSObject 的类方法 performSelectorInBackground:withObject: 来创建一个线程。如果你选择thread来实现多线程,那么 NSThread 就是官方推荐优先选用的方式。
Cocoa operations是基于 Obective-C实现的,类 NSOperation 以面向对象的方式封装了用户需要执行的操作,我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,因为NSOperation已经为我 们封装了这些事情。 NSOperation 是一个抽象基类,我们必须使用它的子类。iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation。
Grand Central Dispatch (GCD): iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,它的关注点更高:如何在多个 cpu 上提升效率。

最后,既然说道多线程的开发,难免会在多线程之间进行通讯;

利用NSObject的一些类方法,可以轻松搞定。

在应用程序主线程中做事情:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array
在指定线程中做事情:
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array

在当前线程中做事情:

//Invokes a method of the receiver on the current thread using the default mode after a delay.

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

performSelector:withObject:afterDelay:inModes:
取消发送给当前线程的某个消息
cancelPreviousPerformRequestsWithTarget:

cancelPreviousPerformRequestsWithTarget:selector:object:
如在我们在某个线程中下载数据,下载完成之后要通知主线程中更新界面等等,可以使用如下接口:- (void)myThreadMainMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// to do something in your thread job
...
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
[pool release];
}

新增内容:

iOS开发中的多线程,无疑是个很重要的知识点,要想把握多线程这块,就要学会以下这些。
一、进程
在移动端,一个app就是一个进程,在内存中占用一定的空间。
在计算机里,一个程序就是一个进程,同样也占用内存空间。
iOS同一时间点只有一个进程在使用CPU,只是系统把这个时间片分割地非常短,造成一种多个进程同时在执行的假象。

二、线程
一个进程的执行,必然从一个主线程开始。整个应用可以由单个主线程运行,但是涉及到一些耗时的任务,例如打开淘宝app,必然要加载一大堆的图片。
这时,如果只有单线程执行,程序必须等着图片都加载完毕才能继续往下执行,期间用户的交互就不起作用,这样用户体验很不好。
所以,这时就衍生出多线程的概念,可以开子线程给那些耗时的任务,在旁边默默地执行,而不影响应用的大局。
主线程,一般用来处理主体的展示(例如控制器的切换)和交互事件。
子线程,一般用来处理耗时的任务。当然,并不是线程越多越好,多线程的使用也是要慎重考虑。

三、同步和异步
我之前一直对同步和异步这个概念理解不清,常常混淆。同步是线程安全呢,还是异步线程安全呢?
今天终于记清楚了这个概念,只要记住一句话——同步,就是同类;异步,就是异类。已经是同类,那肯定是处于同一个线程;异类,那就说明不是一个线程。

四、并行和串行
并行:并发执行
串行:按顺序执行,一个接一个

五、三种常用创建多线程方式
1.NSThread:程序员手动管理线程,而多线程的情况下,线程什么时候执行完毕是未知的,如果管理不好,会造成内存泄露,所以这种方法不提倡。
2.NSOperation\NSOperationQueue。这两个类必须是搭配使用的,将操作放入操作队列中,依次执行。
使用步骤:创建NSOperation;添加NSOperation到NSOperationQueue
优点:更加面向对象;可以控制最大并发数 maxConcurrentOperationCount,使用这个属性可以保证同一时间内最大的并发数;添加任务(Operation)之间的依赖 addDependency,使用这个属性可以控制一个Operation必须在其依赖的Operation执行完毕后才调用。
技术分享
3.GCD(官方推荐使用,纯C语言)
调用同步(异步)执行的方法,传入要并行(串行)执行的队列参数,执行方法内的block代码。说白了就是同一时间有一个还是多个线程执行,就看调用的方法和传入的队列类型。
队列类型:
全局队列:所有添加到全局队列中的任务都是并发执行(同时执行,可能会开启多个线程)
串行队列:所有添加到串行队列中的任务都是按顺序执行(开一条线程)
主队列:所有添加到主队列中的任务都是在主线程中执行的(跟方法名没有关系)
同步还是异步,取决于方法名(不影响主队列,影响全局队列、串行队列)
同步:dispatch_sync,在当前线程执行任务,不会开启新的线程
异步:dispatch_async,在其他线程执行任务,会开启新的线程
代码demo演示:从组合学上说,总是共有四种情况:串行-同步、串行-异步、并行-同步、并行-异步。
串行-同步:显然一直只有一个线程在执行(这个就是真正意义上单线程)
串行-异步:可能会产生多个线程,但是同一时间只有一个线程在执行(异步虽然会产生多个不同线程,但是同一时间只有一个线程在执行)
并行-同步:同一时间点有多个相同的线程在执行
并行-异步:同一时间有多个不同的线程在执行(这是真正意义上的多线程)

下面就只举两个例子,剩下的2种情况举一反三就是了

技术分享

技术分享

而主队列的使用,常常用来执行完子线程后,要讲数据返回主线程来进行处理。比如开启子线程下载某个资源,下载完毕需要回调到主线程来展示。可以在子线程完成的时候调用以下的方法返回主线程,同时能够将子线程得到的参数传给处理的selector方法里执行。
技术分享
4、开启后台线程
技术分享

我对多线程的理解和分类

标签:

原文地址:http://blog.csdn.net/hbblzjy/article/details/51235990

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!