iOS多线程

  • 概念
        进程:进程是指系统中正在运行的一个程序。(每个进程之间是独立的,每个进程运行在其专用且受保护的内存空间里)
        线程:一个进程想要执行任务,必须有线程(每一个进程至少有一条线程),线程是进程的基本执行单元(一个进程的所有任务都在线程中执行)
       线程的串行:一个线程中任务的执行时串行的(按顺序执行) (可以认为线程是进程的一条执行路径)
       多线程:一个进程中可以开启多条线程,每条线程可以 并行执行不同的任务
       多线程原理:
       同一时间,CPU只能处理一条线程,只有一条线程在执行。
       多现场并发执行,其实是CPU快速的在多条线程之间调度(切换)
       如果CPU调度线程的时间足够快,就会造成多线程并发执行的假象
     
       开启线程非常多,会产生的问题:
       1 CPU会在N条线程之间调度,CPU在线程的开销越大,消耗大量的CPU资源
       2 每条线程被调度执行的频次会降低(线程执行效率低)
       3 程序设计更加复杂:比如线程之间的通信,线程的数据共享

       多线程的优点:
       1 能够适当提高程序的执行效率
       2 能够适当提高资源利用率(CPU、内存利用率)
 
  • 多线程在iOS中应用
    主线程:一个iOS程序运行后,默认开启一条线程,成为“主线程”或“UI线程”
   主线程的作用:
      1 显示/刷新UI界面
      2 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
       不要将耗时操作放到主线程中,严重卡住主线程,严重影响UI流畅度
   子线程:将耗时操作放到子线程(后台线程,非主线程)

  • 线程的生命周期和安全问题

1生命周期

2 安全问题
   加锁:锁定是代码,同一时间只有一个线程可以读取次代码。
@synchronized(锁对象){ // 开始加锁(只能是一把锁:一般用self)

} // 解锁
互斥锁的优缺点:
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源

互斥锁的使用前提:多条线程抢夺同一块资源

相关专业术语:线程同步
线程同步的意思是:多条线程按顺序地执行任务
互斥锁,就是使用了线程同步技术

OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁
atomic加锁原理

@property (assign, atomic) int age;

- (void)setAge:(int)age

{

    @synchronized(self) {

        _age = age;

    }

}


  • pthread
      通用,C语言,程序员管理线程的生命周期(使用难度大,跨平台), 几乎不用
      

  •  NSThread
       使用更加面向对象,简单易用,可直接操作线程对象,OC语言,程序员管理线程的生命周期, 偶尔使用 
  1. NSThread的创建


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

    [thread start];

          主线程相关用法                                                                                     + (NSThread *)mainThread; // 获得主线程

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

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

            获得当前线程                                                                             NSThread *current = [NSThread currentThread];

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

    - (BOOL)setThreadPriority:(double)p;

    - (double)threadPriority;

    + (BOOL)setThreadPriority:(double)p;

    + (double)threadPriority;

    • 线程的名字
  3. - (NSString *)name;

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

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

    • 隐式创建并启动线程
  5.  上述2种创建线程方式的优缺点                                                                                                                                                                   优点:简单快捷                                                                                                                                                                                     缺点:无法对线程进行更详细的设置


    [self performSelectorInBackground:@selector(run) withObject:nil];

  6. 线程中的通信

//回到主线程  

[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];

  //去到任意线程

 [self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#> modes:<#(NSArray *)#>]




  • GCD
       代替NSThread等线程技术,充分利用多核技术,C语言,自动管理, 经常使用
      优点:
      GCD是苹果公司为多核的并行运算提出的解决方案
      GCD会自动利用更多的CPU内核
      GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
       只需要告诉GCD想要执行的任务,不需要线程管理代码
  1. 任务和队列:                                                                                             任务:执行的操作                                                                                       队列:用来存放任务
    定制任务,将任务添加到队列当中。(GCD自动将队列中的任务放到对应的线程中,遵循FIFO原则)
         队列的类型:
         并发队列:可以让多个任务并发, 只有在异步函数下才有效
         串行队列:可以让多任务一个接一个执行
         决定任务的执行方式
    2. GCD的2个用来执行任务的函数      
用同步的方式执行任务(当前线程中执行)

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);


用异步的方式执行任务(在另外一条线程中执行, 有开线程的能力

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

    3.GCD默认提供了全局并发队列,供整个应用使用,不需要手动创建


dispatch_queue_t dispatch_get_global_queue(

dispatch_queue_priority_t priority, // 队列的优先级

unsigned long flags); // 此参数暂时无用,用0即可

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列


全局并发队列的优先级
#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 // 后台


4. 获取串行队列(2种途径)

使用dispatch_queue_create函数创建串行队列

dispatch_queue_t

dispatch_queue_create(const char *label, // 队列名称 

dispatch_queue_attr_t attr); // 队列属性,一般用NULL即可

dispatch_queue_t queue = dispatch_queue_create("cn.itcast.queue", NULL); // 创建

dispatch_release(queue); // 非ARC需要释放手动创建的队列

使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列

dispatch_queue_t queue = dispatch_get_main_queue()


从子线程回到主线程

dispatch_async(

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

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

      dispatch_async(dispatch_get_main_queue(), ^{

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

        });

});


5. GCD的其它函数

延迟执行


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    // 2秒后异步执行这里的代码..   

});


一次性代码


static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

    // 只执行1次的代码(这里面默认是线程安全的)

});


队列组:

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 执行1个耗时的异步操作

});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 执行1个耗时的异步操作

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    // 等前面的异步操作都执行完毕后,回到主线程...

});



  • NSOperation 
       基于GCD,比GCD更加简单实用,面向对象,OC语言,自动管理, 经常使用

    
使用NSOperation 的子类 创建任务   NSInvocationOperation 、 NSBlockOperation

1、封装任务
    NSInvocationOperation *opration1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil];

    NSInvocationOperation *opration2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];

    NSBlockOperation *blockOperation = [ NSBlockOperation blockOperationWithBlock ^{
       NSLog ( @"%@——1----" ,[ NSThread currentThread ]);
    }];

    [ blockOperation addExecutionBlock:^{
       
NSLog ( @"%@—2-------" ,[ NSThread currentThread ]);
    }];

以上有三个操作( opration1、 opration2、 blockOperation )  4个任务( download、 run、 %@——1——、 %@——2----

2、创建队列

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

// 最大并发数   同一时间只能做 2 件事   控制开线程的个数  queue.maxConcurrentOperationCount = 2;//2---3

// 任务在队列中的优先级
    /*
     NSOperationQueuePriorityVeryLow = -8L,
     NSOperationQueuePriorityLow = -4L,
     NSOperationQueuePriorityNormal = 0,
     NSOperationQueuePriorityHigh = 4,
     NSOperationQueuePriorityVeryHigh = 8
     */
opration1.queuePriority = NSOperationQueuePriorityNormal;

操作依赖
// 操作依赖   在添加到队列之前   ---- 可以再不同队列间的 operation 之间添加依赖   不能相互依赖
//opration1 依赖 opration2  opration2 执行完毕后才能   执行 opration1
    [opration1 addDependency:opration2];

    // 操作的监听
   
// blockOperation 中的操作完成后 在当前线程中执行下面操作
    blockOperation.
completionBlock = ^{

    };

//添加到队列中---------》异步执行    多个任务一定是并行操作[queue addOperation:opration1];
[queue addOperation:opration2];
[queue addOperation:blockOperation];



自定义NSOperation





创建属性
/**
 * 
下载图片的队列
 */

@property (nonatomic, strong) NSOperationQueue *queue;
/** key:url value:operation对象 */
@property (nonatomic, strong) NSMutableDictionary *operations;
/** key:url value:image对象*/
@property (nonatomic, strong) NSMutableDictionary *images;

懒加载
- (NSArray *)apps
{
   
if (!_apps) {
       
NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
       
       
NSMutableArray *appArray = [NSMutableArray array];
       
for (NSDictionary *dict in dictArray) {
           
HMApp *app = [HMApp appWithDict:dict];
            [appArray
addObject:app];
        }
       
_apps = appArray;
    }
   
return _apps;
}

- (
NSOperationQueue *)queue
{
   
if (!_queue) {
       
_queue = [[NSOperationQueue alloc] init];
       
_queue.maxConcurrentOperationCount = 3; // 最大并发数 == 3
    }
   
return _queue;
}

- (
NSMutableDictionary *)operations
{
   
if (!_operations) {
       
_operations = [NSMutableDictionary dictionary];
    }
   
return _operations;
}

- (
NSMutableDictionary *)images
{
   
if (!_images) {
       
_images = [NSMutableDictionary dictionary];
    }
   
return _images;
}



cell中下载图片的代码
// 显示图片
   
// 保证一个url对应一个HMDownloadOperation
   
// 保证一个url对应UIImage对象
   
   
UIImage *image = self.images[app.icon];
   
if (image) { // 内存缓存中有图片
        cell.
imageView.image = image;
    }
else { // 内存缓存中没有图片, 得下载
//        cell.imageView.image = [UIImage imageNamed:@"57437179_42489b0"];
       
       
HMDownloadOperation *operation = self.operations[app.icon];
       
if (operation) { // 正在下载
           
// ... 暂时不需要做其他事
           
        }
else { // 没有正在下载
           
// 创建操作
            operation = [[
HMDownloadOperation alloc] init];
            operation.
url = app.icon;
            operation.
delegate = self;
            operation.
indexPath = indexPath;
            [
self.queue addOperation:operation]; // 异步下载
           
           
self.operations[app.icon] = operation;
        }
    }

#pragma mark - HMDownloadOperationDelegate
- ( void )downloadOperation:( HMDownloadOperation *)operation didFinishDownload:( UIImage *)image
{
   
// 1. 移除执行完毕的操作
    [
self . operations removeObjectForKey :operation. url ];
   
   
if (image) {
       
// 2. 将图片放到缓存中 (images)
       
self . images [operation. url ] = image;
       
       
// 3. 刷新表格
        [
self . tableView reloadRowsAtIndexPaths : @[ operation. indexPath ] withRowAnimation : UITableViewRowAnimationNone ];
       
       
// 3. 将图片写入沙盒
//        NSData *data = UIImagePNGRepresentation(image);
//        [data writeToFile:@"" atomically:<#(BOOL)#>];
    }
   
}

tableView滚动的时候的操作
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
   
// 开始拖拽
   
// 暂停队列
    [
self.queue setSuspended:YES];
}

- (
void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    [
self.queue setSuspended:NO];
}



自定义NSOperation的代码
HMDownloadOperation.h
@class HMDownloadOperation;

@protocol HMDownloadOperationDelegate <NSObject>
@optional
- (
void)downloadOperation:(HMDownloadOperation *)operation didFinishDownload:(UIImage *)image;
@end

@interface HMDownloadOperation : NSOperation
@property (nonatomic, copy) NSString *url;
@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic, weak) id<HMDownloadOperationDelegate> delegate;
@end


HMDownloadOperation.m
/**
 * 
main方法中实现具体操作
 */

- (
void)main
{
   
@autoreleasepool {
       
if (self.isCancelled) return;
       
       
NSURL *downloadUrl = [NSURL URLWithString:self.url];
       
NSData *data = [NSData dataWithContentsOfURL:downloadUrl]; // 这行会比较耗时
       
       
if (self.isCancelled) return;
       
       
UIImage *image = [UIImage imageWithData:data];
       
       
if (self.isCancelled) return;
       
       
if ([self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) {
           
dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程, 传递图片数据给代理对象
                [
self.delegate downloadOperation:self didFinishDownload:image];
            });
        }
    }
}

        



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值