iOS学习之多线程


在iOS中提供了4中多线程的方式

以下4中方式:

pthread使用较少



pthread 
这是一套通用的多线程api,适用于linux、UNIX、windows 等系统  跨平台技术,可移植 纯c语言

     NSThread *current = [NSThread currentThread];

1、创建一个线程id
pthread_t threadId;
pthrad_create(&threadId,NULL,run,NULL);

void *run(void *data)
{
     return NULL;
}


NSThread
一个NSThread对象就代表一条线程
使用更加面向对象,可以直接操作线程对象,oc 语言   程序员进行管理 线程的生命周期

主线程相关用法
NSThread *current = [NSThread currentThread];
NSThread *mainThread = [NS]
判断是不是主线程
[NSThread isMainThread];

线程的优先级
NSThread threadPriority
-(BOOL)setThreadPriority:(double);
调度优先级的取值范围是0.0 — 1.0 值越大 优先级越高



1、创建线程
*1
NSThread *thread = [[ NSThread alloc ] initWithTarget : self  selector : @selector (run:) object : @"aaa" ];
// 开启线程
[thread start];

*2 创建后就执行  自动启动
无法对线程进行更详细的设置
[NSThread detachNewThreadS elector : @selector (run:) object : @"aaa" ]; ]

3*// 隐式创建
[self
performSelectorInBackground:@selector(run:)withObject:@"aa"];

-(void)run
{
     
}

线程的生命周期
新建——————》线程对象进入可调度线程池(就绪状态)——————》运行状态(如果cpu调度当前线程)——————》调用sleep方法 当前线程从调度池 ,但是该线程仍然在内存中 —————》当阻塞结束 当前进程进入就绪状态

线程任务执行完毕、异常退出、强制退出  都是 线程进入死亡状态(仍然在内存中)
//阻塞线程
+(void)sleepUntiData:(NSDate:)date;
+   (void)sleepForTimeInterval:(NSTimeInterval)ti;
强制停止线程
+(void)exit;   线程进入死亡状态


线程加锁(线程同步)—》多条线程按顺序的执行任务  互斥锁 就是使用了线程同步技术
NSLock *lock = [[NSLock alloc]init];
线程读之前加锁(只能用一把锁)



  @synchronized(self){ 加锁
     大括号内的代码有线程安全问题代码要进行加锁操作

}大括号结束解锁


线程间通信
两个进行线程间通信的方法
下面方法在主线程中执行 settingImage 方法
[self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];

下面方法是在 onThread指定的线程中执行某个方法
[self performSelector:<#(SEL)#> onThread:<#(NSThread *)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#> modes:<#(NSArray *)#>]

对ui界面的操作要在主线程中执行,例如以下载图片为例可以使用
下面方法的意思是:在主线程中调用imageView 的 setImage:方法将imageView的image设置为 下载好的image
其中 waitUntilDone设置为NO为 调用完这个方法会立即返回 
   waitUntilDone设置为YES 这个方法会在主线程执行完setImage:方法后返回
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];

[ self . imageView performSelectorOnMainThread : @selector (setImage:) withObject :image waitUntilDone : NO ];




GCD     Grand Central Dispatch  牛逼的中枢调度器

优势:
1、多核的并行运算提出的解决方案
2、会自动利用更多的cpu内核
3、自动关了线程的生命周期(创建线程、调度任务、销毁线程)
4、不需要编写任何线程管理代码   只需要告诉GCD 要执行的任务  GCD 会自动创建队列执行任务

使用:两个步骤  
定制任务  
添加定制任务到队列中 —》
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循 FIFO  先进先出


//凡是函数名种带有create\copy\new\retain等字眼,都需要在不需要使用这个数据的时候进行release
// GCD的数据类型在ARC环境下不需要再做release
// CF(Core Foundation) 的数据类型在 ARC 环境下还是需要再做 release



/**
 * 
全局并发队列
 */

#define global_queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 )
/**
 * 
主队列
 */
#define main_queue dispatch_get_main_queue()


基本使用:(同步函数和异步函数———决定了要不要开启新的线程)
1、2个用来执行任务的函数
同步:在当前线程中执行任务 不具备开启新线程的能力
dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)

异步:在另一条线程中执行  具备了开启新线程的能力
dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
特殊情况:异步函数的任务添加在主队列中(往主队列中添加一个同步任务), 任务在主线程执行,不会开启新的线程

2、队列
分为两大类(并发和串行————决定了任务的执行方式)
并发队列:可以让多个任务并发同时执行(自动开启多条线程)   在异步函数中执行

串行队列:让任务一个接着一个执行

并发队列:GCD默认已经提供了全局的并发队列供整应用程序使用,不需要手动创建


获取一个全局的默认优先级的并发队列
dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

添加任务到队列中执行任务

1、 dispatch_async 异步函数往并发队列中添加任务—————》同时开启了3个子线程
- (void)asyncGlobalQueue
{
   // 获取一个全局的默认优先级的并发队列
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
   
   
// 2.添加任务到队列中执行
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片1-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片2-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片3-----%@", [NSThreadcurrentThread]);
    });
   // 总结:同时开启了3个线程
}

2、用dispatch_async异步函数 往串行队列中添加任务————————》只开了一个线程执行任务
- (void)asyncSerialQueue
{
   
// 1.创建串行队列
   
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
   
   
// 2.添加任务到队列中执行
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片1-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片2-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_async(queue, ^{
       
NSLog(@"----下载图片3-----%@", [NSThreadcurrentThread
]);
    });
   //总结:只开1个线程执行任务
}


3、用dispatch_sync同步函数往并发队列中添加任务—————》不会开启新的线程,并发队列失去了并发的功能
- (void)syncGlobalQueue
{
   
// 1.获得全局的并发队列
   
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
   
   
// 2.添加任务到队列中执行
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片1-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片2-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片3-----%@", [NSThreadcurrentThread]);
    });
   
   
// 总结:不会开启新的线程,并发队列失去了并发的功能
}


4、 dispatch_sync 同步函数往串行列中添加任务—————》不会开启新的线程
- (void)syncSerialQueue
{
   // 1.创建串行队列(串行队列)
   dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue",NULL);
   
   
// 2.添加任务到队列中执行
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片1-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片2-----%@", [NSThreadcurrentThread]);
    });
   
dispatch_sync(queue, ^{
       
NSLog(@"----下载图片3-----%@", [NSThreadcurrentThread]);
    });
   
   
// 3.释放资源
//    dispatch_release(queue);   // MRC(ARC)
   
   
// 总结:不会开启新的线程
}





5、 dispatch_sync 同步函数 , 在主线程中往主队列中添加任务 : 任务无法往下执行———————>产生死锁(死循环)
- (void)syncMainQueue
{
   
// 1.获得主队列
   
dispatch_queue_t queue = dispatch_get_main_queue();
   
   // 2.添加任务到队列中执行
     (queue, ^{
       NSLog(@"----下载图片1-----%@", [NSThreadcurrentThread]);
    });
//    dispatch_sync(queue, ^{
//        NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
//    });
//    dispatch_sync(queue, ^{
//        NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
//    });

}

6、 使用dispatch_async异步函数,在主线程中往主队列中添加任务
特殊情况:异步函数的任务添加在主队列中(往主队列中添加一个同步任务), 任务在主线程执行,不会开启新的线程
- ( void )asyncMainQueue
{
    // 1.获得主队列(串行队列)
    dispatch_queue_t queue = dispatch_get_main_queue ();
   
   
// 2. 添加任务到队列中 执行
   
dispatch_async (queue, ^{
       
NSLog ( @"---- 下载图片 1-----%@" , [ NSThread currentThread ]);
    });
}


同步函数(sync)
并发队列:不会开启线程
串行队列:不会开启线程
异步函数(async)
并发队列:开启多个线程
串行队列:开启一条线程


使用GCD进行异步图片下载
1、获取全局并发队列
    dispatch_queue_tqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
2、调用异步(同步)函数下载图片
dispatch_async(queue, ^{//开启异步线程下载图片
       NSLog(@"当前线程%@",[NSThreadcurrentThread]);
       
NSURL *url = [NSURLURLWithString:@"http://www.res.meizu.com/resources/www_image/weixin.jpg"];
       
NSData *data = [NSDatadataWithContentsOfURL:url];
       
       
UIImage *image = [UIImageimageWithData:data];
      
3、回到主队列刷新图片
       dispatch_async(dispatch_get_main_queue(), ^{
           
self.imageView.image= image;
        });
    });

GCD的其他用法
1、延迟执行 
*1  [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

*2 利用GCD 进行延迟执行(推荐)
dispatch_queue_t queue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//    /**
//     *  2 秒钟后   在队列 queue 中执行 block 中的任务
//     */
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
//   
//    });


2、一次性代码   使某一段代码在整个程序运行过程中只执行一次,用于创建单  dispatch_once
static id _instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
      _instance = [[self alloc]init];
});

3、队列组  当程序需要执行完多个耗时操作后 在执行某个其他操作的时候使用
如:下载两张图片  下载完成后进行拼接显示在imageView上
*1、 创建一个组
dispatch_group_t group = dispatch_group_create();
*2、开启一个任务下载图片1
__block  UIImage *image1 = nil;
   
//开启一个任务下载图片1
   
dispatch_group_async(group, global_queue, ^{
        image1 = [
self imageWithUrl:@"1"];
    });
*3 开启一个任务下载图片2
   __block UIImage *image2 = nil;
   
//开启一个任务下载图片2
    dispatch_group_async(group, global_queue, ^{
    image2 = [self imageWithUrl:@"2"];
    });
4* 等待group中的所有任务都执行完毕后  在执行其他操作
    dispatch_group_notify(group, main_queue, ^{
// 合并图
     UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
     [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
     [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
     UIImageView *imageView = nil;
     imageView.image = UIGraphicsGetImageFromCurrentImageContext();
  
    });






NSOperation

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

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

   NSInvocationOperation *opration2 = [[NSInvocationOperationalloc]initWithTarget:selfselector:@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 = [[NSOperationQueuealloc]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
    [opration1addDependency:opration2];

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

    };

//添加到队列中---------》异步执行   多个任务一定是并行操作[queueaddOperation:opration1];
[queueaddOperation:opration2];
[queueaddOperation: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 = [NSArrayarrayWithContentsOfFile:[[NSBundlemainBundle]pathForResource:@"apps.plist"ofType:nil]];
       
       
NSMutableArray *appArray = [NSMutableArrayarray];
       
for (NSDictionary*dict in dictArray) {
           
HMApp *app = [HMAppappWithDict:dict];
            [appArray
addObject:app];
        }
       
_apps = appArray;
    }
   
return _apps;
}

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

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

- (
NSMutableDictionary*)images
{
   
if (!_images) {
       
_images = [NSMutableDictionarydictionary];
    }
   
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 = [[
HMDownloadOperationalloc]init];
            operation.
url= app.icon;
            operation.
delegate= self;
            operation.
indexPath= indexPath;
            [
self.queueaddOperation: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.queuesetSuspended:YES];
}

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



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

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

@interfaceHMDownloadOperation : 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 = [NSURLURLWithString:self.url];
       
NSData *data = [NSDatadataWithContentsOfURL:downloadUrl];// 这行会比较耗时
       
       
if (self.isCancelled)return;
       
       
UIImage *image = [UIImageimageWithData:data];
       
       
if (self.isCancelled)return;
       
       
if ([self.delegaterespondsToSelector:@selector(downloadOperation:didFinishDownload:)]) {
           
dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程,传递图片数据给代理对象
                [
self.delegatedownloadOperation:selfdidFinishDownload:image];
            });
        }
    }





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值