zoukankan      html  css  js  c++  java
  • iOS的三种多线程技术:


    1. NSThread
    1> 类方法 detachNewThreadSelector
    直接启动线程,调用选择器方法

    2> 成员方法 initWithTarget
    需要使用start方法,才能启动实例化出来的线程

    优点:简单
    缺点:
    1 控制线程的生命周期比较困难
    2 控制并发线程数
    3 先后顺序困难
    例如:下载图片(后台线程) -> 滤镜美化(后台线程) -> 更新UI(主线程)

    2. NSOperation
    1> NSInvocationOperation
    2> NSBlockOperation

    定义完Operation之后,将操作添加到NSOperationQueue即可启动线程,执行任务

    使用:
    1> setMaxConcurrentOperationCount 可以控制同时并发的线程数量
    2> addDependency 可以指定线程之间的依赖关系,从而达到控制线程执行顺序的目的

    1 // Dependency依赖
    2     // 提示:依赖关系可以多重依赖
    3     // 注意:不要建立循环依赖
    4     [op2 addDependency:op1];
    5     [op3 addDependency:op2];


    提示:
    要更新UI,需要使用[NSOperationQueue mainQueue]addOperationWithBlock:
    在主操作队列中更新界面


    3. GCD
    1) 全局global队列
    方法:dispatch_get_global_queue(获取全局队列)
    优先级:DISPATCH_QUEUE_PRIORITY_DEFAULT
    所有添加到主队列中的任务都是并发执行的


    2) 串行队列
    方法:dispatch_queue_create(创建串行队列,串行队列不能够获取)
    所有添加到串行队列中的任务都是顺序执行的

    3) 主队列
    主线程队列
    方法:dispatch_get_main_queue(获取主队列)
    所有添加到主队列中的任务都是在主线程中执行的

    在gcd中,同步还是异步取决于任务执行所在的队列,更方法名没有关系

    获取队列的方法:

    全局队列(可能会开启多条线程)
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    串行队列(只可能会开启一条线程)
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

    主队列
    dispatch_get_main_queue();


    多线程使用注意事项:
    线程使用不是无节制的
    iOS中的主线程的堆栈大小是1M
    从第二个线程开始都是512KB
    这些数值不能通过编译器开关或线程API函数更改
    只有主线程有直接修改UI的能力

     1 static Ticket *SharedInstance;
     2 
     3 @implementation Ticket
     4 
     5 /**
     6  实现单例模型需要做三件事情
     7  
     8  1. 使用全局静态变量记录住第一个被实例化的对象
     9     static Ticket *SharedInstance
    10  
    11  2. 重写allocWithZone方法,并使用dispatch_once_t,从而保证在多线程情况下,
    12     同样只能实例化一个对象副本
    13  
    14  3. 建立一个以shared开头的类方法实例化单例对象,便于其他类调用,同时不容易引起歧义
    15     同样用dispatch_once_t确保只有一个副本被建立
    16  
    17  
    18  关于被抢夺资源使用的注意事项
    19  
    20  在多线程应用中,所有被抢夺资源的属性需要设置为原子属性
    21  系统会在多线程抢夺时,保证该属性有且仅有一个线程能够访问
    22  
    23  注意:使用atomic属性,会降低系统性能,在开发多线程应用时,尽量不要资源
    24  另外,atomic属性,必须与@synchronized(同步锁)一起使用
    25  
    26  */
    27 
    28 // 使用内存地址实例化对象,所有实例化方法,最终都会调用此方法
    29 // 要实例化出来唯一的对象,需要一个变量记录住第一个实例化出来的对象
    30 + (id)allocWithZone:(NSZone *)zone
    31 {
    32     // 解决多线程中,同样只能实例化出一个对象副本
    33     static dispatch_once_t onceToken;
    34     dispatch_once(&onceToken, ^{
    35         SharedInstance = [super allocWithZone:zone];
    36     });
    37 
    38     return SharedInstance;
    39 }
    40 
    41 // 建立一个单例对象,便于其他类调用
    42 + (Ticket *)sharedTicket
    43 {
    44     static dispatch_once_t onceToken;
    45     dispatch_once(&onceToken, ^{
    46         SharedInstance = [[Ticket alloc]init];
    47     });
    48     
    49     return SharedInstance;
    50 }
    51 
    52 @end
      1 @property (weak, nonatomic) UITextView *textView;
      2 
      3 @property (strong, nonatomic) NSOperationQueue *queue;
      4 
      5 @end
      6 
      7 @implementation MainViewController
      8 
      9 #pragma mark 追加多行文本框内容
     10 - (void)appendContent:(NSString *)text
     11 {
     12     // 1. 取出textView中的当前文本内容
     13     NSMutableString *str = [NSMutableString stringWithString:self.textView.text];
     14     
     15     // 2. 将text追加至textView内容的末尾
     16     [str appendFormat:@"%@
    ", text];
     17     
     18     // 3. 使用追加后的文本,替换textView中的内容
     19     [self.textView setText:str];
     20     
     21     // 4. 将textView滚动至视图底部,保证能够及时看到新追加的内容
     22     NSRange range = NSMakeRange(str.length - 1, 1);
     23     [self.textView scrollRangeToVisible:range];
     24 }
     25 
     26 - (void)viewDidLoad
     27 {
     28     [super viewDidLoad];
     29 
     30     // 建立多行文本框
     31     UITextView *textView = [[UITextView alloc]initWithFrame:self.view.bounds];
     32     // 不能编辑
     33     [textView setEditable:NO];
     34     [self.view addSubview:textView];
     35     self.textView = textView;
     36 
     37     // 预设可以卖30张票
     38     [Ticket sharedTicket].tickets = 30;
     39     
     40     // 实例化操作队列
     41     self.queue = [[NSOperationQueue alloc]init];
     42     
     43     // 开始卖票
     44     [self operationSales];
     45 }
     46 #pragma mark - NSThread卖票
     47 - (void)threadSaleTicketWithName:(NSString *)name
     48 {
     49     // 使用NSThread时,线程调用的方法千万要使用@autoreleasepool
     50     @autoreleasepool {        
     51         while (YES) {
     52             @synchronized(self) {
     53                 if ([Ticket sharedTicket].tickets > 0) {
     54                     [Ticket sharedTicket].tickets--;
     55                     
     56                     NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name];
     57                     
     58                     // 更新UI
     59                     [self performSelectorOnMainThread:@selector(appendContent:) withObject:str waitUntilDone:YES];
     60                 } else {
     61                     break;
     62                 }
     63             }
     64             
     65             // 模拟休息
     66             if ([name isEqualToString:@"thread-1"]) {
     67                 [NSThread sleepForTimeInterval:1.0f];
     68             } else {
     69                 [NSThread sleepForTimeInterval:0.1f];
     70             }
     71         }
     72     }
     73 }
     74 
     75 - (void)threadSales
     76 {
     77     [NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-1"];
     78     [NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-2"];
     79 }
     80 
     81 #pragma mark - NSOperation卖票
     82 - (void)operationSaleTicketWithName:(NSString *)name
     83 {
     84     while (YES) {
     85         // 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分
     86         @synchronized(self) {
     87             // 判断是否还有票
     88             if ([Ticket sharedTicket].tickets > 0) {
     89                 [Ticket sharedTicket].tickets--;
     90                 
     91                 // 提示,涉及到被抢夺资源的内容定义方面的操作,千万不要跨线程去处理
     92                 NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name];
     93                 
     94                 // 更新UI
     95                 [[NSOperationQueue mainQueue]addOperationWithBlock:^{
     96                     [self appendContent:str];
     97                 }];
     98             } else {
     99                 NSLog(@"卖票完成 %@ %@", name, [NSThread currentThread]);
    100                 break;
    101             }
    102         }
    103         // 模拟卖票休息
    104         if ([name isEqualToString:@"op-1"]) {
    105             [NSThread sleepForTimeInterval:0.6f];
    106         } else {
    107             [NSThread sleepForTimeInterval:0.4f];
    108         }
    109     }
    110 }
    111 
    112 - (void)operationSales
    113 {
    114     // 提示,operation中没有群组任务完成通知功能
    115     // 两个线程卖票
    116     [self.queue setMaxConcurrentOperationCount:2];
    117     
    118     [self.queue addOperationWithBlock:^{
    119         [self operationSaleTicketWithName:@"op-1"];
    120     }];
    121     [self.queue addOperationWithBlock:^{
    122         [self operationSaleTicketWithName:@"op-2"];
    123     }];
    124     [self.queue addOperationWithBlock:^{
    125         [self operationSaleTicketWithName:@"op-3"];
    126     }];
    127 }
    128 
    129 #pragma mark - GCD卖票
    130 - (void)gcdSaleTicketWithName:(NSString *)name
    131 {
    132     while (YES) {
    133         // 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分
    134         @synchronized(self) {
    135             if ([Ticket sharedTicket].tickets > 0) {
    136                 
    137                 [Ticket sharedTicket].tickets--;
    138                 
    139                 // 提示内容
    140                 NSString *str = [NSString stringWithFormat:@"剩余票数 %d, 线程名称 %@", [Ticket sharedTicket].tickets, name];
    141                 
    142                 // 更新界面
    143                 dispatch_sync(dispatch_get_main_queue(), ^{
    144                     [self appendContent:str];
    145                 });
    146             } else {
    147                 break;
    148             }
    149         }
    150         
    151         // 模拟线程休眠
    152         if ([name isEqualToString:@"gcd-1"]) {
    153             [NSThread sleepForTimeInterval:1.0f];
    154         } else {
    155             [NSThread sleepForTimeInterval:0.2f];
    156         }
    157     }
    158 }
    159 
    160 - (void)gcdSales
    161 {
    162     // 1) 创建全局队列
    163     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    164     
    165     // 2) 创建三个个异步任务分别卖票
    166 //    dispatch_async(queue, ^{
    167 //        [self gcdSaleTicketWithName:@"gcd-1"];
    168 //    });
    169 //    dispatch_async(queue, ^{
    170 //        [self gcdSaleTicketWithName:@"gcd-2"];
    171 //    });
    172 //    dispatch_async(queue, ^{
    173 //        [self gcdSaleTicketWithName:@"gcd-3"];
    174 //    });
    175     
    176     // 3. GCD中可以将一组相关联的操作,定义到一个群组中
    177     // 定义到群组中之后,当所有线程完成时,可以获得通知
    178     // 1) 定义群组
    179     dispatch_group_t group = dispatch_group_create();
    180     
    181     // 2) 定义群组的异步任务
    182     dispatch_group_async(group, queue, ^{
    183         [self gcdSaleTicketWithName:@"gcd-1"];
    184     });
    185     dispatch_group_async(group, queue, ^{
    186         [self gcdSaleTicketWithName:@"gcd-2"];
    187     });
    188     
    189     // 3) 群组任务完成通知
    190     dispatch_group_notify(group, queue, ^{
    191         NSLog(@"卖完了");
    192     });
    193 }
    194 
    195 @end
  • 相关阅读:
    ireport制作小技巧
    Spring 自动装配 Bean
    Toad创建DBLINKsop
    Spring 读书笔记-----使用Spring容器(一)
    Spring读书笔记-----Spring的Bean之Bean的基本概念
    关于iOS开发中info.plist文件的解读
    iOS常用的第三方库GitHub地址
    NSUserDefault的使用
    论坛收集
    iOS开发的一些奇巧淫技
  • 原文地址:https://www.cnblogs.com/yyh123/p/3356161.html
Copyright © 2011-2022 走看看