13-多线程


1.NSThread
一、创建和启动线程简单说明

一个NSThread对象就代表一条线程

创建、启动线程

(1) NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

[thread start];

// 线程一启动,就会在线程thread中执行self的run方法
  sleepForTimeInterval  休眠
主线程相关用法
+ (NSThread *)mainThread; // 获得主线程 

- (BOOL)isMainThread; // 是否为主线程

+ (BOOL)isMainThread; // 是否为主线程

 

其他用法

获得当前线程

NSThread *current = [NSThread currentThread];

 

线程的调度优先级:调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高

+ (double)threadPriority;

+ (BOOL)setThreadPriority:(double)p;

 

设置线程的名字

- (void)setName:(NSString *)n;

- (NSString *)name;

 

其他创建线程的方式

(2)创建线程后自动启动线程   [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

(3)隐式创建并启动线程  [self performSelectorInBackground:@selector(run) withObject:nil];

上述2种创建线程方式的优缺点

优点:简单快捷

缺点:无法对线程进行更详细的设置


二、代码示例
NSThread  *thread=[[NSThread alloc]initWithTarget:self selector:@selector( thread1 )object:@"线程A"]; 
//为线程设置一个名称
thread.name=@"线程A"; 
//开启线程    
[thread start];
---------------------------------------------- 回到主线程设置UI---------------------------------------------
//回到主线程执行方法
[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
必须在调用的方法中加上自动释放池!来释放掉我们在操作过程中的内存!否则会发生内存泄漏
- ( void )thread1:( NSString *)threadName {

   
@autoreleasepool {
       
       
       
for ( int i= 0 ; i< 50 ; i++) {
           
NSLog ( @" 多线程: name:%@  %d" ,threadName,i);
        }
       
        //在多线程中如果要循环运行需要添加 [[ NSRunLoop currentRunLoop ] run ];

    }

}



2.NSOperation
一、简介

NSOperation

NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程

NSOperation和NSOperationQueue实现多线程的具体步骤:

(1)先将需要执行的操作封装到一个NSOperation对象中

(2)然后将NSOperation对象添加到NSOperationQueue中

(3)系统会⾃动将NSOperationQueue中的NSOperation取出来

(4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏

 2.NSOperation的子类

NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类

使用NSOperation⼦类的方式有3种:

(1)NSInvocationOperation

(2)NSBlockOperation

(3)自定义子类继承NSOperation,实现内部相应的⽅法



NSOperationQueue

NSOperationQueue的作⽤:NSOperation可以调⽤start⽅法来执⾏任务,但默认是同步执行的

如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

添加操作到NSOperationQueue中,自动执行操作,自动开启线程

---------------------------------------------------------------------------------------------

1、并发数
(1)并发数:同时执⾏行的任务数.比如,同时开3个线程执行3个任务,并发数就是3
(2)最大并发数:同一时间最多只能执行的任务的个数。
(3)最⼤大并发数的相关⽅方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 
说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。
注意:num的值并不代表线程的个数,仅仅代表线程的ID。
提示:最大并发数不要乱写(5以内),不要开太多,一般以2~3为宜,因为虽然任务是在子线程进行处理的,但是cpu处理这些过多的子线程可能会影响UI,让UI变卡。

---------------------------------------------------------------------------------------------

2、队列的取消,暂停和恢复

 (1)取消队列的所有操作

 - (void)cancelAllOperations;

提⽰:也可以调用NSOperation的- (void)cancel⽅法取消单个操作

 (2)暂停和恢复队列

- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列

- (BOOL)isSuspended; //当前状态

(3)暂停和恢复的适用场合:在tableview界面,开线程下载远程的网络界面,对UI会有影响,使用户体验变差。那么这种情况,就可以设置在用户操作UI(如滚动屏幕)的时候,暂停队列(不是取消队列),停止滚动的时候,恢复队列。

---------------------------------------------------------------------------------------------

3、操作优先级

 (1)设置NSOperation在queue中的优先级,可以改变操作的执⾏优先级

- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;

 (2)优先级的取值

NSOperationQueuePriorityVeryLow = -8L,

NSOperationQueuePriorityLow = -4L,

NSOperationQueuePriorityNormal = 0,

NSOperationQueuePriorityHigh = 4,

NSOperationQueuePriorityVeryHigh = 8 

说明:优先级高的任务,调用的几率会更大。

---------------------------------------------------------------------------------------------

4、操作依赖

(1)NSOperation之间可以设置依赖来保证执行顺序,⽐如一定要让操作A执行完后,才能执行操作B,可以像下面这么写

[operationB addDependency:operationA]; // 操作B依赖于操作

(2)可以在不同queue的NSOperation之间创建依赖关系 

注意:不能循环依赖(不能A依赖于B,B又依赖于A)。

---------------------------------------------------------------------------------------------

------------------------------------------------- 回到主线程设置UI-----------------------------------------------
 

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperationWithBlock:^{

    // 1.执行一些比较耗时的操作

    

    // 2.回到主线程

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        

    }];

}];
 
二、NSOperation 线程创建三种方式
-------------------------------------- NSInvocationOperation方式-----------------------------------  
// 一、使用 Operation 的子类创建多线程
   
   
//1. 创建队列
   
NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ];
   
// 先暂停队列 , 为了让队列里面的线程一起执行 后面开启
    [queue
setSuspended : YES ];
   
   
//2. 创建 NSInvocationOperation 对象,封装操作
   
NSInvocationOperation *operation1=[[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (test1) object : @"op1" ];
   
   
NSInvocationOperation *operation2=[[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (test2) object : @"op2" ];

   
//3. 添加到队列
    [queue
addOperation :operation1];
    [queue
addOperation :operation2];
   
---------------------------------------------------- block方式-------------------------------------------------     
// 二、使用 block 方式创建多线程
    // 直接添加到队列 , 但是不能使之优先级 , 优先级默认是 NSOperationQueuePriorityNormal
    [queue addOperationWithBlock:^{
       @autoreleasepool{  
       for(inti=0; i<50; i++) {
           NSLog(@"op3:i:%d",i);
        }
       }
    }];


---------------------------------------- 自定义继承NSOperation子类--------------------------------------      
// 三、自定义继承 NSOperation 子类创建多线程
    MyOperation *operation4 = [[ MyOperation alloc ] initWithTarget : self selector : @selector (test4)];
   
// 添加到队列
    [queue
addOperation :operation4];
   
   
// 线程添加完成后 恢复队列
    [queue setSuspended:NO];
自定义NSOperations的子类
{

    id _target;
   
SEL _sel;
}

- (id)initWithTarget:(id)target selector:(SEL)sel;

-----------------------------------------------------------------------------------------------------------------

- ( id )initWithTarget:( id )target selector:( SEL )sel {
   
   
self = [ super init ];
   
   
if ( self ) {
       
       
_target = target;   //viewCtrl
       
_sel = sel; //thread4
       
    }
   
   
return self ;
}

// 新开启的线程,调用的入口方法
- (
void )main {
   
   
@autoreleasepool {
       
        [
_target performSelector : _sel withObject : nil ];
    }
   
}
---------------------------------------------------------------------------------------------
必须在调用的方法中加上自动释放池!来释放掉我们在操作过程中的内存!否则会发生内存泄漏

- (void)test1
{
   
@autoreleasepool{
       
for (int i=0; i<50; i++) {
           
NSLog(@"op1:i:%d",i);
        }
    }
}

- (
void)test2
{
   
@autoreleasepool{
       
for (int i=0; i<50; i++) {
           
NSLog(@"op2:i:%d",i);
        }
    }
}

- (
void)test4
{
   
@autoreleasepool{
       
for (int i=0; i<50; i++) {
           
NSLog(@"op4:i:%d",i);
        }
    }
}


3.GCD

一、简单介绍

1.什么是GCD?

全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”

纯C语言,提供了非常多强大的函数

 

2.GCD的优势

GCD是苹果公司为多核的并行运算提出的解决方案

GCD会自动利用更多的CPU内核(比如双核、四核)

GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码


3.提示

(1)GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,但任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入。
(2)GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。
(3)GCD中的函数大多数都以dispatch开头。

一. 队列和任务
1 任务 :需要执行什么操作
     *  block 来封装任务

2 队列   :存放任务

说明:全局并发队列的优先级

#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)

#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

// 获取并行全局队列
dispatch_queue_t globalQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
   
// 获取串行主队列
//注意:往主队列中同步添加任务时,容易引起线程死锁
//主队列异步添加任务时不会开启线程
dispatch_queue_t mainQueue = dispatch_get_main_queue ();
   
// 自定义队列 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并行队列
dispatch_queue_t serialQueue = dispatch_queue_create ( "serialQueue" , DISPATCH_QUEUE_SERIAL );

---------------------------------------------------------------------------------------------

二. 执行任务的函数
  1 同步执行  : 不会开启新线程

     dispatch_sync...


  2 异步执行  : 会开启新线程

     dispatch_async...


---------------------------------------------------------------------------------------------
 
三.常见的组合(掌握)

1> dispatch_async + 全局并发队列

2> dispatch_async + 自己创建的串行队列

 

---------------------------------------------------------------------------------------------
 
四.线程间的通信(掌握)
------------------------------------------------- 回到主线程设置UI-----------------------------------------------

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

   // 执行耗时的异步操作...

    

    

   dispatch_async(dispatch_get_main_queue(), ^{

       // 回到主线程,执行UI刷新操作

   });

});


---------------------------------------------------------------------------------------------

五、线程同步(掌握)

1.实质:为了防止多个线程抢夺同一个资源造成的数据安全问题


2.实现:给代码加一个互斥锁(同步锁)

@synchronized(self) {

    // 被锁住的代码

}

注意:锁定1份代码只用1把锁,用多把锁是无效的


---------------------------------------------------------------------------------------------

六、GCD加锁 同步锁

 static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        _instace = [[self alloc] init];

    });


七.延迟执行(掌握)

1> perform....

// 3秒后自动回到当前线程调用selfdownload:方法,并且传递参数:@"http://555.jpg"

[self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];


2> dispatch_after...

// 任务放到哪个队列中执行

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

double delay = 3// 延迟多少秒

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{

    // 3秒后需要执行的任务

});


八.一次性代码(掌握)

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

    // 这里面的代码,在程序运行过程中,永远只会执行1

});


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值