iOS多线程技术---pthread、NSThread、NSOperation、GCD

多线程技术

process进程:在系统中运行的一个应用程序;每个进程都是独立的;有专有的内存空间

thread线程:程序的一段执行序列,进程的不部分;

             特点:1、进程只分配内存空间,并不执行任务

                         2、每个进程至少有一个线程,该线程叫做主线程

                         3、线程是进程的基本执行单元,进程的所有任务都是在线程中执行

                         4、每个线程中得任务的执行都是串行的

 

多线程并发:一个进程中多条线程并发执行任务;

            特点:   1、提高程序的执行效率,提高资源利用率

                        2、同一时间,CPU只能处理一条线程

                        3、多线程并发时,CPU在多条线程间快速切换

                        4、线程切换的 速度很快,就造成了多线程的并发执行

                        5、开启线程需要内存空间,线程太多会造成调度消耗太多资源

                        6、线程过多会降低每条线程被调度的频率(线程执行效率降低)

 

多线程的应用:

        主线程:显示刷新UI界面、处理UI事件;耗时任务(如下载)放在子线程中

        判定方法在哪个线程中执行:NSLog(@“当前的线程:”,[NSThread currentThread]);

 

四种多线程技术:pthread   NSThread   GCD  NSOperation

 

1.pthread

      基于c语言的API ,可编写多平台应用,使用难度大,需要手动创建销毁线程,可自定义功能少

pthread_t pthread;

void *task(void *data){

NSLog(@“当前子线程:%@“,NSThread currentThread];

return 0;

}

 

pthread_create(&pthread,NULL,task,NULL);

 

2.NSThread

  • 基于OC,相对于pthread使用简单,面向对象
  • 缺点:需要自己管理线程的生命周期,线程同步,加锁、开锁,管理多个线程比较困难

 

手动创建线程的三种方法:

1、NSThread实例方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);(只创建

2、NSThread类方法:+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; (创建并运行

3、NSThread实例方法:- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);

注:selector: 线程执行的方法,这个selector只能由一个参数,而且没有返回值

        object: 传给target的唯一参数,也可以为nil

        target: selector消息发送的对象 只有第一个能设置名字、优先级

线程阻塞(延迟):

  • // sleep⽅法⼀:开始睡眠5秒钟

[NSThread sleepForTimeInterval:5];

  • // sleep⽅法二:

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3

[NSThread sleepUntilDate:date];

强制停止线程:

[NSThread exit];

其他常用方法:

  • +(NSThread *)mainThread; //获得主线程
  • -(BOOL)isMainThread;//判断是否是主线程
  • +(double)threadPriority;//获取线程优先级    优先级取值范围0.0~1.0 值越大优先级越高
  • +(BOOL)setThreadPriority:(double)p;//设置优先级
  • -(NSString*)name;//获取线程名字
  • -(void)setName:(NSString*)n;//设置线程名字

 

线程的状态:

  • New(新建) -> Runnable(就绪) --> cpu开始调度当前线程 ---> Running(运行) ---> 调用 sleep方法 ---> Blocked(阻塞) ---> 移除线程池 ---> sleep执行完毕/到时 ---> 进入线程 池 ---> Runnable(就绪)
  • 如果线程任务执行完毕/异常/强制退出 ---> Dead(死亡)

加锁:

  • 尽量避免使用@synchronized,也就是避免多个线程访问同一个资源,因为有了加锁、 解锁需要消耗比较大的cpu资源 
  • 加锁的前提:多个线程同时访问同一个资源的时候才需要加锁(互斥锁) 
  • 线程同步:多条线程在同一条线上执行(按顺序地执行任务) 
  • 尽量讲加锁、资源抢夺的业务逻辑交割服务器端处理,减少移动客户端的压力

synchronized关键字

1、synchronized关键字的作用域有二种:

1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;

2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用

2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){},它的作用域是当前对象;

原子和非原子属性:

  • nonatomic: 非原子 —> 不会在set方法中加锁,这个是推荐方式,会占用资源少
  • atomic:原子 —> 在set方法中加锁,防止多个线程同时执行set方法

 

线程间的通讯

1.在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 

2.线程间通讯的方式:

  • 一个线程传递数据给另一个线程
  • 在一个线程中执行完特定任务后,转到另一个线程继续执行任务

子线程返回主线程的两种方法:

 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

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

***********************************************************************************************

3. GCD

 并行队列只有在执行多个异步任务的时候体现出来并发现象,因为在线程快速切换时体现出并行;

 同步执行的任务 会致使后面的任务提交阻塞;

GCD:Grand Central Dispath 大中央调度

 
  • GCD的基本思想就是将操作(任务)放在队列中去执行
  • 队列负责调度任务执行所在的线程以及具体的执行时间
  • 队列的特点是先进先出,新添加至队列的操作(任务)都会排在队尾
 
GCD的函数都是以dispatch开头的,dispatch的意思是“分配、调度”
 
  • 串行队列中的任务会按顺序执行
  • 并行队列中的任务通常会并发执行,而且无法确定任务的执行顺序
 
dispatch_async表示异步操作,异步操作会新开辟线程来执行任务,而且无法确定任务的执行顺序
dispatch_sync表示同步操作,同步操作不会新开辟线程
 
  1. 在串行队列中执行同步任务:不会新建线程,按顺序执行任务(毫无用处)
  2. 在串行队列中执行异步任务,会新建线程,按顺序执行任务(非常有用)
 
  1. 在并行队列中执行同步任务:不会新建线程,按顺序执行任务(几乎没用)
  2. 在并行队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序(有用,但是很容易出错)
 
全局队列
  • 全局队列是系统的,直接拿过来就可以用,与并行队列类似,但是不能指定队列的名字,调试时无法确认任务所在队列
  • 在全局队列中执行同步任务:不会新建线程,按顺序执行任务
  • 在全局队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序
主队列
  • 如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程。
  • 每一个应用程序只有一个主线程即只有一个主队列
  • 为什么需要再主线程上执行任务呢?因为在ios开发中,所有UI的更新任务都必须在主线程上执行。
  • 主队列中的操作都是在主线程中执行的,不存在异步的概念
  • 在主线程中向 主队列中添加的同步操作会死锁

线程阻塞(延迟):三种方法

 1 //过3秒后做一件事儿,这个做法会导致主线程阻塞,不提倡使用
 2 - (IBAction)sleepDelay:(id)sender
 3 {
 4     NSLog(@"3秒后干件事儿!");
 5     [NSThread sleepForTimeInterval:3];
 6     NSLog(@"干事儿中...");
 7 }
 8 - (IBAction)performAfterDelay:(id)sender
 9 {
10     NSLog(@"3秒后干件事儿!");
11     //在3秒后会启动一个线程去完成任务(调用方法task),此方法并不会阻塞
12     [self performSelector:@selector(task) withObject:nil afterDelay:3];
13     NSLog(@"主线程继续向下执行...");
14 }
15 - (void)task
16 {
17     NSLog(@"干活中...");
18 }
19 //使用GCD实现延迟执行
20 - (IBAction)dispatchAfter:(id)sender
21 {
22     /*
23      1秒 = 1000毫秒
24      1毫秒 = 1000微秒
25      1微秒 = 1000纳秒
26      */
27     //dispatch_time函数的第二个参数单位是纳秒
28     NSLog(@"3秒钟后做事");
29     dispatch_after(
30                    /**
31                     *  延迟的函数
32                     *
33                     *  @param DISPATCH_TIME_NOW 从现在起
34                     *  @param int64_t           延迟描述
35                     *    NSEC_PER_SEC 这个宏的意思是每秒多少纳秒
36                     *  @return 无
37                     */
38         dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC),
39         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
40         ^{
41             NSLog(@"3秒后我开始做需要的事情啦!");
42     });
43 }
View Code

线程间的通讯:

dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执⾏耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{

// 回到主线程,执⾏UI刷新操作
});
});  

dispatch_once  参照:线程安全的单例  http://www.cnblogs.com/ChrisYu/p/4651114.html

小结:

       无论什么队列和什么任务,线程的创建和回收不需要程序员参与,由队列来负责,程序员只需要面对队列和任务。GCD在后端管理这一个线程池,GCD不仅决定着Block代码块将在哪个线程中被执行,而且还可以根据可用的系统资源对这些线程进行管理,从而让开发者从线程管理的工作中解放出来,通过GCD这种集中的管理线程,缓解了大量的线程被创建的问题

 

 

4. NSOperation

 

 

 

单词:

1. global   ge(0) lao(1) bao(3)  全局

2. concurrent   并发

3. queue  队列

 

 

作业:

1. 从网上下载图片并显示到界面上。

要求,下载过程中界面不能死。

用GCD做

 

2. 了解一个第三方框架SDWebImage

 1 - (IBAction)serialOperation
 2 {
 3     //默认OperationQueue中的线程都是并行的
 4     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
 5     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
 6 //    [operation1 start];//不会启动线程,直接在主线程中执行
 7     [queue addOperation:operation1];
 8     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
 9     //[operation2 start];
10     //线程二要依赖线程一,所以线程2会等线程一结束了再执行,按此方式可以让两个线程串行执行
11     [operation2 addDependency:operation1];
12     [queue addOperation:operation2];
13 }
14 
15 - (void)printPlusSignal
16 {
17     for (int i=0; i<10; i++) {
18         [NSThread sleepForTimeInterval:1];
19         NSLog(@"++++++++++++++");
20     }
21 }
22 
23 - (void)printMinusSignal
24 {
25     for (int i=0; i<10; i++) {
26         [NSThread sleepForTimeInterval:1];
27         NSLog(@"----------------");
28     }
29 }
30 
31 - (IBAction)concurrentQueue:(id)sender
32 {
33     //默认OperationQueue中的线程都是并行的
34     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
35     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
36    
37     [queue addOperation:operation1];
38     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
39 
40     [queue addOperation:operation2];
41 }
42 
43 //使用Block提交任务给OperationQueue
44 - (IBAction)blockQueue
45 {
46     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
47     //CallBack 回调
48     NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
49         for (int i=0; i<10; i++) {
50             [NSThread sleepForTimeInterval:1];
51             NSLog(@"++++++++++++++");
52         }
53     }];
54     [queue addOperation:operation1];
55     
56     NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
57         for (int i=0; i<10; i++) {
58             [NSThread sleepForTimeInterval:1];
59             NSLog(@"-------------");
60         }
61     }];
62     [queue addOperation:operation2];
63 }
64 - (IBAction)mainQueue
65 {
66     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
67     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
68         NSLog(@"在子线程中-----%@", [NSThread currentThread]);
69         //获取主队列(回到主线程)
70         NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
71         NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
72             NSLog(@"在主线程中?-----%@", [NSThread currentThread]);
73         }];
74         [mainQueue addOperation:operation2];
75     }];
76     [queue addOperation:operation];
77 }
View Code
原文地址:https://www.cnblogs.com/ChrisYu/p/4649968.html