多线程


多线程概念

  • 程序: 由源代码生成的可执行应用.
  • 进程: 一个正在运行的程序可以看做一个进程. (例如: 正在运行的QQ就是一个进程) , 进程拥有独立运行所需的全部资源.
  • 线程: 程序中独立运行的代码段.  

注:一个进程是由一或多个线程组成. 进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行.

  • 单线程: 
  1. 每个正在运行的程序(即 进程),至少包含一个线程, 这个线程叫 主线程 
  2. 主线程在程序启动时被创建,用于执行mian函数
  3. 只有一个主线程的程序,称作单线程程序
  4. 在单线程程序中,主线程负责执行程序的所有代码(UI 展现以及刷新,网络请求, 本地存储等). 这些代码只能顺序执行, 无法并发执行 . 

  • 多线程: 
  1. 拥有多个线程的程序,称作多线程程序
  2. iOS 允许用户自己开辟新的线程, 相对于主线程来讲, 这些线程, 称作子线程
  3. 可以根据需要开辟若干子线程
  4. 子线程和主线程 都是 独立 的运行单元, 各自的执行互不影响, 因此能够并发执行 

  • 单线程,多线程区别 : 
  1. 单线程程序: 只有一个线程, 即主线程, 代码顺序执行,容易出现代码阻塞(页面假死)
  2. 多线程程序: 有多个线程, 线程间独立运行, 能有效的避免代码阻塞,并且提高程序的运行性能.
注意: iOS关于UI的添加和刷新必须在主线程中操作.

  • iOS多线程实现种类
  1. NSObject
  2. NSThread
  3. NSOperationQueue
  4. GCD

    // 开发中依赖于多线程 : 网络 :( 主线程 :(UI), 子线程 ( 取数据 ))



NSObject 和 NSThread


  - ( void )viewDidLoad {
    [superviewDidLoad];
#pragma mark ---------NSObject 开辟子线程

//    NSObject 开辟子线程
//     参数 1: selector, 子线程执行的代码
//     参数 2: 表示 selector 传递的参数
    [selfperformSelectorInBackground:@selector(sayHi) withObject:@"hahah"];

#pragma mark ---------NSThread 手动开辟子线程
//     NSThread 开辟一个子线程
//     参数 1: target
//     参数 2: action
//     参数 3: 传参
   
NSThread *thread = [[ NSThread alloc ] initWithTarget : self selector : @selector (sayHi) object : nil ];
   
   
// 开启子线程
    [thread
start ];
     // 取消 ( 给线程发送结束消息 , 不会真正的取消掉线程 , 而是标记 这个被取消了 )
    [thread
cancel ];
   
// 立即结束线程
    [
NSThread exit ];
   
// 对于 NSObject NSThread 实现的多线程 , 在任务完成之后 , 线程会被自动释放
   
// 判断一个线程是否正在执行
    [thread
isExecuting ];
   
// 判断一个线程是否完成了任务 ( 是否执行完毕 )
    [thread isFinished];
   
#pragma mark -----------NSThread 自动开辟一个线程
    // 使用 NSThread 自动开辟一个线程
   
// 不需要手动开启线程
    [NSThreaddetachNewThreadSelector:@selector(sayHi) toTarget:selfwithObject:nil];

  // 几秒后执行某件事情
    [self performSelector:@selector(time) withObject:self afterDelay:3.0f];
}
     //让线程休眠2秒
    [ NSThread sleepForTimeInterval :2];

- ( void )sayHi{
   
   
//  [NSThread currentThread]; 获取当前的线程
   
NSLog ( @" %@ " , [ NSThread currentThread ]);
   
//   [NSThread mainThread];  获取主线程
   
NSLog ( @" %@ " , [ NSThread mainThread ]);
   
//   [NSThread isMainThread]  判断当前线程是不是主线程
   
NSLog ( @" %d " , [ NSThread isMainThread ]);
    
#pragma mark ----------NSObject
    //NSObject 中回到主线程去做某事
   
// 参数 1: 回到主线程做的事情
   
// 参数 2: 传递的参数
   
// 参数 3: 知道当前的线程已经结束才去做
    [
self performSelectorOnMainThread : @selector (onMainThread) withObject : nil waitUntilDone : YES ];
   
#pragma mark -----------NSThread 手动
        for ( int i = 0; i < 10000; i++) {
           
NSLog ( @" %d " , i);
           
if (i == 5000) {
               
// 关闭线程
               
// 写在哪里 哪个线程就关闭了 , 注意 不要随意的使用 . 使用的时候一定要注意当前的线程 是否主线程
                [
NSThread exit ];
            }
        }
}

- ( void )onMainThread{
    self.view.backgroundColor = [UIColororangeColor];    
    NSLog ( @"mian --> %@ " , [ NSThread mainThread ]);    
    NSLog ( @"current --> %@" , [ NSThread currentThread ]);
}







NSOperation和NSOperationQueue


  • NSOperation :
  1. NSOperation类, 在MVC中属于M, 是用来封装单个任务相关的代码和数据的抽象类
  2. 因为它抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation) 来执行实际任务.
  3. NSOperation(含子类),  只是一个操作, 本身无主线, 子线程之分, 可在任意线程中使用. 通常与NSOperationQueue结合使用.

  • NSOperationQueue
  1. NSOperationQueue是操作队列,它用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务并行执行
  2. 其中NSOperation可以调节它在队列中的优先级 (使用addDependency: 设置依赖关系)
  3. 当最大并发数设置为1的时候,能实现线程同步 (串行执行);
- ( void )viewDidLoad {
    [superviewDidLoad];  
    //NSOperation 是一个抽象类 , 不能直接使用
   
//NSOperation 类及其子类本身不会进行线程的创建
#pragma mark —————NSInvocationOperation    
    // 通过 NSInvocationOperation 类来创建一个 NSOperation 对象
   
NSInvocationOperation *operation = [[ NSInvocationOperation alloc ] initWithTarget : self selector : @selector (hehehe) object : nil ];
   
    //operation 在单独使用的时候 一定要调用开始方法
    [operation start];

#pragma mark —————NSBlockOperation   
    NSBlockOperation *blockOperation = [ NSBlockOperation blockOperationWithBlock :^{
       
NSLog ( @"block main --> %@" , [ NSThread mainThread ]);
       
NSLog ( @"block current --> %@" , [ NSThread currentThread ]);
    }];
   
    //operation 在单独使用的时候 一定要调用开始方法
    [blockOperation start];
  #pragma mark —————NSOperationQueue   
    NSOperationQueue *queue = [[ NSOperationQueue alloc ] init ];
    
     //队列添加operation子类,并调用方法
    [queue addOperation :operation];
    [queue addOperation:blockOperation]; 

    //依赖关系  (只有参数线程 执行完,才能执行,之前是随机交错进行的)
    [operation addDependency :blockOperation];
   
  
    // 获取 系统提供的队列
   
NSOperationQueue *mainQueue = [ NSOperationQueue mainQueue ];
}

- (
void )hehehe{
   
   
NSLog ( @"hehehe main --> %@" , [ NSThread mainThread ]);
   
NSLog ( @"hehehe current --> %@" , [ NSThread currentThread ]);
   
NSLog ( @" %d " , [ NSThread isMainThread ]);
}

- ( void )touchesBegan:( NSSet <</span>UITouch *> *)touches withEvent:(UIEvent *)event{
   
   
//NSOperationQueue 是一个队列管理器,可以根据operation任务自己,分配线程,自己管理线程的生命周期
   
//在开发过程中,我们不需要管理线程的创建和销毁
   
//NSOperationQueue 创建的是n个并行的线程
   
NSOperationQueue *queue = [[NSOperationQueuealloc]init];
   
   
//最大线程并发数
   
//设置这个参数之后,NSOperationQueue表示同时执行任务的最大数量
   
//即使只执行一个任务,系统有时候也会开辟多个线程去执行这个任务
    queue.
maxConcurrentOperationCount = 1;
   
   
for (int i = 0; i < 10; i++) {
       
NSBlockOperation *blockOperation = [NSBlockOperationblockOperationWithBlock:^{
           
NSLog(@"current --> %@ main --> %@",[NSThreadcurrentThread],[NSThreadmainThread]);
        }];
        [queue
addOperation:blockOperation];
    }
}

@end



GCD

Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称处理系统.

GCD提供函数实现多线程开发,性能更好,功能也更加强大.

核心概念:
  1. 任务: 具有一个订功能的代码段.一般是一个block或者函数
  2. 分发队列: GCD以队列的方式进行工作,FIFO(先入先出队列)
  3. GCD会根据分发队列的类型, 创建合适数量的线程执行队列中的任务

GCD中的两种队列 dispatch_queue:
  1. SerialQueue(串行) : 一次只执行一个任务. Serial queue通常用于同步访问,特定的资源或数据.当你创建多个Serial queue时,虽然他们各自是同步执行的,但Serial queue于Serial queue之间是并发执行的.SerialQueue能实现线程同步.
  2. Concurrent(并发) : 可以并发地执行多个任务,但是遵守FIFO(先入先出队列 )

- ( void )viewDidLoad {
    [
super viewDidLoad ];

#pragma mark ----------GCD 串行队列
//     1) 系统提供的一个串行队列
//    使用系统提供的串行队列 ( 主队列,也就是在主线程里一次执行任务 )
    dispatch_queue_t queue = dispatch_get_main_queue ();
   
//     2) 创建一个串行队列
//     参数 1: 自己创建队列的名 ( 苹果推荐使用反向域名去命名 )
//     参数 2: 系统提供好的一个宏 (DISPATCH_QUEUE_SERIAL=NULL)
//     这种方式创建的队列,会开辟子线程去执行任务
    dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);

#pragma mark ----------GCD 并行队列
   
//     1) 使用系统提供的并行队列
//    参数 1: 表示队列的优先级 (
         DISPATCH_QUEUE_PRIORITY_BACKGROUND,
         DISPATCH_QUEUE_PRIORITY_LOW,
         DISPATCH_QUEUE_PRIORITY_HEIGH,
         DISPATCH_QUEUE_PRIORITY_DEFAULT)
//    参数 2: 系统保留字段
   
dispatch_queue_t queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND , 0);
//     2) 创建并行队列
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);


#pragma mark ----------GCD 功能

  •     //使用dispatch_async() 向队列添加任务,任务会排队执行
        // 任务虽会顺序排队执行,但如果用并发队列( CONCURRENT ),可能输出顺序不一样.
    dispatch_async (queue, ^{
       
NSLog ( @"1 mian --> %@, current --> %@" ,[ NSThread mainThread ],[ NSThread currentThread ]);
    });
 
   
dispatch_async (queue, ^{
       
NSLog ( @"2 mian --> %@, current --> %@" ,[ NSThread mainThread ],[ NSThread currentThread ]);
    });

   
dispatch_async (queue, ^{
       
NSLog ( @"3 mian --> %@, current --> %@" ,[ NSThread mainThread ],[ NSThread currentThread ]);
    });

  •     // dispatch_after();
    // 往队列中添加任务 , 任务不但会排队 , 还会在延迟的时间点执行
   
dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )(3.0f * NSEC_PER_SEC )), dispatch_get_main_queue (), ^{
       
       
NSLog ( @" 已经 3 秒之后了 " );
    });

  •     //dispatch_apply();
    // 往队列中添加任务 , 任务会重复执行 n
   
// 参数 1: 一共执行次数
   
// 参数 2: 执行的队列
   
// 参数 3: 当前索引
    dispatch_apply(3, queue, ^(size_t index) {
        NSLog(
@" %zu " , index);
    });


  •     //分组   
     // 创建一个分组
   
dispatch_group_t group = dispatch_group_create ();
   
// 创建一个队列
   
dispatch_queue_t queue = dispatch_queue_create ( "000" , DISPATCH_QUEUE_CONCURRENT );
   
   
// 向分组中添加一个任务
   
dispatch_group_async (group, queue, ^{
       
NSLog ( @"1" );
    });
   
   
// 向分组添加 最后执行的任务 ( 不能添加为第一个 )
   
dispatch_group_notify (group, queue, ^{
       
NSLog ( @"last one" );
    });
    //将任务添加到队列,此任务执行的时候,其他任务停止执行,所以它输出顺序不改变
    dispatch_barrier_async (queue, ^{
       
NSLog ( @" 不变位置的 2" );
    });
   
   
dispatch_group_async (group, queue, ^{
       
NSLog ( @"3" );
    });
}


  • dispatch_once() //将任务添加到队列, 但任务在程序运行过程中,只执行一次
 //创建单例类
#import "MyObject.h"

static MyObject *object = nil ;
@implementation MyObject

+ (
MyObject *)sharedMyObject{
   
   
// 表示同意时间内 , 只有一个线程可以访问 block 块里面的内容
   
//dispatch_once 系统封装好的代码块
   
static dispatch_once_t onceToken;
   
dispatch_once (&onceToken, ^{
       
if ( object == nil ) {
       
object = [ MyObject new ];
    }
    });
   
return object ;
}
@end

  • dispatch_sync() //将任务添加到队列, block不执行完,下面代码不会执行
  • dispatch_async() //将任务添加到队列,不等内部block执行完,就去执行下面代码
  • dispatch_async_f() //将任务添加到队列, 任务是函数,非Block



线程间通信

线程间通信分为两种:
  1. 主线程进入子线程 (前面的方法都可以)
  2. 子线程回到主线程
#pragma mark ----------NSObject
  -(void)viewDidLoad{
    //NSObject 中回到主线程去做某事
    // 参数 1: 回到主线程做的事情
   
// 参数 2: 传递的参数
   
// 参数 3: 知道当前的线程已经结束才去做
    [selfperformSelectorOnMainThread:@selector(onMainThread) withObject:nilwaitUntilDone:YES];
}
- ( void )onMainThread{
    self.view.backgroundColor = [UIColororangeColor];    
    NSLog ( @"mian --> %@ " , [ NSThread mainThread ]);    
    NSLog ( @"current --> %@" , [ NSThread currentThread ]);
}
#pragma mark —————GCD
- ( void )loadData{
   
NSURLSession *session = [ NSURLSession sharedSession ];
   
   
NSURLSessionTask *task = [session dataTaskWithURL :[ NSURL URLWithString : @"https://www.baidu.com" ] completionHandler :^( NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       
if (error == nil ) {
           
dispatch_async ( dispatch_get_main_queue (), ^{
               
// 这里去做更新 UI 的事情
            });
        }
    }];
}


  • 线程互斥
  1. 多线程并行编程中,线程间同步与互斥是一个很有技巧也是很容易出错的地方.
  2. 线程间互斥应对的是这种场景: 多个线程操作统一资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值