【网络】多线程--NSThread、GCD、NSOperationQueue

GCD

1.什么是GCD?

GCD是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue 中,GCD就可以生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现,因此可以统一管理,也可以执行任务,这样就比以前的线程更有效率。
用异步函数往并发队列中添加任务

-(void)testGlobalQueue
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
}

//2.用dispatch_async异步函数往串行队列添加任务

-(void)testAsyncQueue
{
    //1.创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("mosuyanxue", NULL);
    //2.添加任务到串行队列中
    dispatch_async(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
}


//用同步函数往串行队列添加任务

-(void)testSyncQueue
{
    dispatch_queue_t queue = dispatch_queue_create("mosuyanxue", NULL);
    dispatch_sync(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
}


//4.用dispatch_sync同步函数往并发队列中添加任务

<span style="background-color: rgb(255, 255, 255);">-(void)testSyncQueue2
{
    dispatch_queue_t queue = dispatch_queue_create("mosuyanxue", NULL);
    dispatch_sync(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
}</span>


//5.线程异步下载图片 主线程刷新UI

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    __block UIImage * image;
    UIImageView * imgView = [[UIImageView alloc]initWithFrame:CGRectMake(10, 100, 100, 100)];
    [self.view addSubview:imgView];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSURL * url = [NSURL URLWithString:@"http://pic80.nipic.com/file/20151013/12964311_203654101785_2.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
         image = [UIImage imageWithData:data];
        NSLog(@"%@",data);
        dispatch_async(dispatch_get_main_queue(), ^{
            imgView.image = image;
        });
    });
}



延迟执行

[NSThread sleepForTimeInterval:3];//这个尽量不要使用,会卡死当前线程。
    NSLog(@"%@",@"task");
    dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"%@",@"task sleep 3s");
    });


一次性代码

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        <#code to be executed once#>
    });

可以用作单例的实现方法


队列组

-(void)testQueueGroup
{
    //组
    dispatch_group_t group = dispatch_group_create();
    //队列
    dispatch_queue_t queue = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_async(group, queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
            NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"%@",@"完成");
    });
}
2.NSThread

NSThread 有两种直接创建方式:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument

+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument

第一个是实例方法,第二个是类方法

[cpp]  view plain  copy
  1. 1、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];  
  2. 2、NSThread* myThread = [[NSThread alloc] initWithTarget:self  
  3.                                         selector:@selector(doSomething:)  
  4.                                         object:nil];  
  5. [myThread start];  

参数的意义:

selector :线程执行的方法,这个selector只能有一个参数,而且不能有返回值。

target  :selector消息发送的对象

argument:传输给target的唯一参数,也可以是nil

第一种方式会直接创建线程并且开始运行线程,第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息

threadPriority

@property (readonlyBOOL isMainThread NS_AVAILABLE(10_5, 2_0);

+ (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main

+ (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0);

@property (nullablecopyNSString *name NS_AVAILABLE(10_5, 2_0);

@property double threadPriority NS_AVAILABLE(10_6, 4_0); // To be deprecated; use qualityOfService below


下载图片例子

  1. @implementation ViewController  
  2.   
  3. -(void)downloadImage:(NSString *) url{  
  4.     NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  
  5.     UIImage *image = [[UIImage alloc]initWithData:data];  
  6.     if(image == nil){  
  7.           
  8.     }else{  
  9.         [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];  
  10.     }  
  11. }  
  12.   
  13. -(void)updateUI:(UIImage*) image{  
  14.     self.imageView.image = image;  
  15. }  
  16.   
  17.   
  18. - (void)viewDidLoad  
  19. {  
  20.     [super viewDidLoad];  
  21.       
  22. //    [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];  
  23.     NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage:) object:kURL];  
  24.     [thread start];  
  25. }  
  26.   
  27. - (void)didReceiveMemoryWarning  
  28. {  
  29.     [super didReceiveMemoryWarning];  
  30.     // Dispose of any resources that can be recreated.  
  31. }  
  32.   
  33. @end  

线程间通讯

线程下载完图片后怎么通知主线程更新界面呢?

[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];

performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:

用:performSelector:onThread:withObject:waitUntilDone: 



线程同步

  1. - (void)run{  
  2.     while (TRUE) {  
  3.         // 上锁  
  4. //        [ticketsCondition lock];  
  5.         [theLock lock];  
  6.         if(tickets >= 0){  
  7.             [NSThread sleepForTimeInterval:0.09];  
  8.             count = 100 - tickets;  
  9.             NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);  
  10.             tickets--;  
  11.         }else{  
  12.             break;  
  13.         }  
  14.         [theLock unlock];  
  15. //        [ticketsCondition unlock];  
  16.     }  
  17. }  
如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。
上面例子我使用了两种锁,一种NSCondition ,一种是:NSLock。 NSCondition我已经注释了

线程的顺序执行
线程的顺序执行

他们都可以通过

        [ticketsCondition signal]; 发送信号的方式,在一个线程唤醒另外一个线程的等待。

比如:

  1. tickets = 100;  
  2.     count = 0;  
  3.     theLock = [[NSLock alloc] init];  
  4.     // 锁对象  
  5.     ticketsCondition = [[NSCondition alloc] init];  
  6.     ticketsThreadone = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  7.     [ticketsThreadone setName:@"Thread-1"];  
  8.     [ticketsThreadone start];  
  9.       
  10.     ticketsThreadtwo = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];  
  11.     [ticketsThreadtwo setName:@"Thread-2"];  
  12.     [ticketsThreadtwo start];  
  13.       
  14.     NSThread *ticketsThreadthree = [[NSThread alloc] initWithTarget:self selector:@selector(run3) object:nil];  
  15.     [ticketsThreadthree setName:@"Thread-3"];  
  16.     [ticketsThreadthree start];      
  17.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  18.     // Override point for customization after application launch.  
  19.     self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  
  20.     self.window.rootViewController = self.viewController;  
  21.     [self.window makeKeyAndVisible];  
  22.     return YES;  
  23. }  
  24.   
  25. -(void)run3{  
  26.     while (YES) {  
  27.         [ticketsCondition lock];  
  28.         [NSThread sleepForTimeInterval:3];  
  29.         [ticketsCondition signal];  
  30.         [ticketsCondition unlock];  
  31.     }  
  32. }  
  33.   
  34. - (void)run{  
  35.     while (TRUE) {  
  36.         // 上锁  
  37.         [ticketsCondition lock];  
  38.         [ticketsCondition wait];  
  39.         [theLock lock];  
  40.         if(tickets >= 0){  
  41.             [NSThread sleepForTimeInterval:0.09];  
  42.             count = 100 - tickets;  
  43.             NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);  
  44.             tickets--;  
  45.         }else{  
  46.             break;  
  47.         }  
  48.         [theLock unlock];  
  49.         [ticketsCondition unlock];  
  50.     }  
  51. }  
其他同步

我们可以使用指令 @synchronized 来简化 NSLock的使用,这样我们就不必显示编写创建NSLock,加锁并解锁相关代码。
- (void)doSomeThing:(id)anObj
{
    @synchronized(anObj)
    {
        // Everything between the braces is protected by the @synchronized directive.
    }
}

3.NSOperationQueue

将需要执行的操作封装到NSOperation对象

NSOperation对象放入NSOperationQueue

系统自动queue中取operation

将取出的操作放入新线程执行。


NSOperation是抽象类 不具备封装操作能力,需要使用子类

使用NSOperation子类的方式有三种。

NSInvocationOperation

NSBlockOperation

自定义子类继承NSOperation实现内部相应的方法


-(void)testNSInvocationOperation
{
    //1,创建操作对象 封装要执行的任务
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoad) object:nil];
    //2.执行操作
    [operation start];
    //总结:默认情况下如果操作没有放队列中 那么默认是主线程
}


-(void)downLoad

{

    NSLog(@"%@",@"逗我呢");

}


//BlockOperation

-(void)testBlockOperation

{

    NSBlockOperation * operation = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"1%@",[NSThread currentThread]);

    }];

    [operation addExecutionBlock:^{

        NSLog(@"2%@",[NSThread currentThread]);

    }];

    [operation addExecutionBlock:^{

        NSLog(@"3%@",[NSThread currentThread]);

    }];

    [operation start];

    //是否开启了新线程 取决于任务的个数。任务=1 主线程 任务>1开启新线程

}





/如果想异步执行任务 则需要queue队列的协助。

-(void)testOperationQueue

{

    //1.封装操作

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

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

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

    //2.创建队列

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

    //3.将操作添加到队列中

    [queue addOperation:operation1];

    [queue addOperation:operation2];

    [queue addOperation:operation3];

}

-(void)download

{

    NSLog(@"%@",@"download");

}

-(void)run

{

    NSLog(@"%@",@"runing");

}


operation最大并发数 MaxConcurrentOperationCount = 5以内 2-3为好。


//取消队列的所有操作

    [queue cancelAllOperations];

    //暂停

    [queue setSuspended:YES];

    

    //operation的优先级取值

    //NSOpertaion之间可以设置操作依赖。

    //如让a执行完之后 才能执行操作B。可以这么写:

    [operation1 addDependency:operation2];

可以在不同的operationQueue之间设置依赖关系,但是不能相互依赖。




自定义Operation

创建类继承自NSOperation

然后在.h中定义需要的属性,代理和代理方法。在.m文件中main函数写自己需要的操作。

代码如下。

@implementation CustomOperation

//在main方法中实现操作

-(void)main

{

    NSURL * url = [NSURL URLWithString:self.url];

    NSData * data = [NSData dataWithContentsOfURL:url];

    UIImage * image = [UIImage imageWithData:data];

    if (self.delegate && [self.delegate respondsToSelector:@selector(downloadOperation:didFinishDownload:)]) {

        [self.delegate downloadOperation:self didFinishDownload:image];

    }

}

@end


#import <Foundation/Foundation.h>

@class CustomOperation;

#import <UIKit/UIKit.h>

@protocol CustomOperationDelegate<NSObject>

@optional

-(void)downloadOperation:(CustomOperation *)operation didFinishDownload:(UIImage *)image;

@end


@interface CustomOperation : NSOperation

@property (nonatomic,strongNSIndexPath *indexpath;

@property (nonatomic,copyNSString * url;

@property (nonatomic,assignid<CustomOperationDelegate> delegate;

@end



检测网络状态

  -(void)testNetStatus

{

    //根据用户的网络状态进行处理

    /*

     wifi /3G自动下载高清图片

     低速网络 下载缩略图

     没有网络 显示离线缓存数据

     苹果官方提供了一个Reachability 可检测网络状态。

     */

    //wifi

    Reachability * wifi = [Reachability reachabilityForLocalWiFi];

    //手机自带网络功能

    Reachability * conn= [Reachability reachabilityForInternetConnection];

    

    if ([wifi currentReachabilityStatus ]!= NotReachable) {

        NSLog(@"%@",@"wifi");

    }else  if ([conn currentReachabilityStatus] != NotReachable){

        NSLog(@"%@",@"有手机网络");

    }else{

        NSLog(@"%@",@"没有网络");

    }

}


Reachability还可以实时监控网络状态。添加通知,如果网络有问题就通知。





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值