您的位置:首页 > 理论基础 > 计算机网络

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

2016-02-19 00:39 555 查看
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、[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];  

2、NSThread* myThread = [[NSThread alloc] initWithTarget:self  

                                        selector:@selector(doSomething:)  

                                        object:nil];  

[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


下载图片例子

@implementation ViewController  

  

-(void)downloadImage:(NSString *) url{  

    NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];  

    UIImage *image = [[UIImage alloc]initWithData:data];  

    if(image == nil){  

          

    }else{  

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

    }  

}  

  

-(void)updateUI:(UIImage*) image{  

    self.imageView.image = image;  

}  

  

  

- (void)viewDidLoad  

{  

    [super viewDidLoad];  

      

//    [NSThread detachNewThreadSelector:@selector(downloadImage:) toTarget:self withObject:kURL];  

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

    [thread start];  

}  

  

- (void)didReceiveMemoryWarning  

{  

    [super didReceiveMemoryWarning];  

    // Dispose of any resources that can be recreated.  

}  

  

@end  

线程间通讯

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

[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
performSelectorOnMainThread是NSObject的方法,除了可以更新主线程的数据外,还可以更新其他线程的比如:

用:performSelector:onThread:withObject:waitUntilDone: 

线程同步

- (void)run{  

    while (TRUE) {  

        // 上锁  

//        [ticketsCondition lock];  

        [theLock lock];  

        if(tickets >= 0){  

            [NSThread sleepForTimeInterval:0.09];  

            count = 100 - tickets;  

            NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);  

            tickets--;  

        }else{  

            break;  

        }  

        [theLock unlock];  

//        [ticketsCondition unlock];  

    }  

}  
如果没有线程同步的lock,卖票数可能是-1.加上lock之后线程同步保证了数据的正确性。
上面例子我使用了两种锁,一种NSCondition ,一种是:NSLock。 NSCondition我已经注释了

线程的顺序执行

线程的顺序执行

他们都可以通过

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

比如:

tickets = 100;  

    count = 0;  

    theLock = [[NSLock alloc] init];  

    // 锁对象  

    ticketsCondition = [[NSCondition alloc] init];  

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

    [ticketsThreadone setName:@"Thread-1"];  

    [ticketsThreadone start];  

      

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

    [ticketsThreadtwo setName:@"Thread-2"];  

    [ticketsThreadtwo start];  

      

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

    [ticketsThreadthree setName:@"Thread-3"];  

    [ticketsThreadthree start];      

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  

    // Override point for customization after application launch.  

    self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];  

    self.window.rootViewController = self.viewController;  

    [self.window makeKeyAndVisible];  

    return YES;  

}  

  

-(void)run3{  

    while (YES) {  

        [ticketsCondition lock];  

        [NSThread sleepForTimeInterval:3];  

        [ticketsCondition signal];  

        [ticketsCondition unlock];  

    }  

}  

  

- (void)run{  

    while (TRUE) {  

        // 上锁  

        [ticketsCondition lock];  

        [ticketsCondition wait];  

        [theLock lock];  

        if(tickets >= 0){  

            [NSThread sleepForTimeInterval:0.09];  

            count = 100 - tickets;  

            NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThread currentThread] name]);  

            tickets--;  

        }else{  

            break;  

        }  

        [theLock unlock];  

        [ticketsCondition unlock];  

    }  

}  

其他同步

我们可以使用指令 @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还可以实时监控网络状态。添加通知,如果网络有问题就通知。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: