三种多线程

来源:互联网 发布:学中医的软件 编辑:程序博客网 时间:2024/06/05 04:55
1、三种多线程技术的对比                          
•NSThread:–优点:NSThread 比其他两个轻量级,使用简单–缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销 
•NSOperation:–不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上–NSOperation是面向对象的 •GCD:–Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术–GCD是基于C语言的
 
2.三种多线程技术的实现                          
2.1. NSThread的多线程技术,                                                   
 1> 类方法直接开启后台线程,并执行选择器方法
    detachNewThreadSelector
  
 // 新建一个线程,调用@selector方法2 3 [NSThread detachNewThreadSelector:@selector(bigDemo) toTarget:self withObject:nil];
 
 
 
 2> 成员方法,在实例化线程对象之后,需要使用start执行选择器方法
    initWithTarget
 
   // 成员方法    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(bigDemo) object:nil];        // 启动start线程    [thread start];
 
 对于NSThread的简单使用,可以用NSObject的performSelectorInBackground替代
 
 // performSelectorInBackground是将bigDemo的任务放在后台线程中执行2 3     [self performSelectorInBackground:@selector(bigDemo) withObject:nil];
 
 同时,在NSThread调用的方法中,同样要使用autoreleasepool进行内存管理,否则容易出现内存泄露。
 // 自动释放池 2  3     // 负责其他线程上的内存管理,在使用NSThread或者NSObject的线程方法时,一定要使用自动释放池 4  5     // 否则容易出现内存泄露。 6  7     @autoreleasepool { 8  9 10 11   }
 
 
2.2 NSOperation,面向对象的多线程技术                                            
 
 1> 使用步骤:
    1) 实例化操作
  // 实例化操作队列
    _queue = [[NSOperationQueue alloc] init];

        a) NSInvocationOperation
   NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(opAction) object:nil];
   // 如果使用start,会在当前线程启动操作4 //    [op1 start];    // 一旦将操作添加到操作队列,操作就会启动
    [_queue addOperation:op1];
 
    
        b) NSBlockOperation
 1 #pragma mark 模仿下载网络图像 2 - (IBAction)operationDemo3:(id)sender 3 { 4     // 1. 下载 5     NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ 6         NSLog(@"下载 %@" , [NSThread currentThread]); 7     }]; 8     // 2. 滤镜 9     NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{10         NSLog(@"滤镜 %@" , [NSThread currentThread]);11     }];12     // 3. 显示13     NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{14         NSLog(@"更新UI %@" , [NSThread currentThread]);15     }];16     17     // 添加操作之间的依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动18     // 依赖关系可以跨线程队列实现19     // 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。20     [op2 addDependency:op1];21     [op3 addDependency:op2];22 //    [op1 addDependency:op3];23     24     [_queue addOperation:op1];25     [_queue addOperation:op2];26     [[NSOperationQueue mainQueue] addOperation:op3];27 }
 
 
    2) 将操作添加到队列NSOperationQueue即可启动多线程执行
    [_queue addOperation:op1];
   [_queue addOperation:op2];
 
 2> 更新UI使用主线程队列
 
    //两方式    [NSOpeationQueue mainQueue] addOperation ^{  };    [[NSOperationQueue mainQueue] addOperation:op3];
 
 
 3> 操作队列的setMaxConcurrentOperationCount
    可以设置同时并发的线程数量!
    // 控制同时最大并发的线程数量
    [_queue setMaxConcurrentOperationCount:2];
 
    提示:此功能仅有NSOperation有!
 
 4> 使用addDependency可以设置任务的执行先后顺序,同时可以跨操作队列指定依赖关系
   
     // 添加操作之间的依赖关系,所谓“依赖”关系,就是等待前一个任务完成后,后一个任务才能启动   // 依赖关系可以跨线程队列实现     // 提示:在指定依赖关系时,注意不要循环依赖,否则不工作。 [op2 addDependency:op1];  [op3 addDependency:op2];  [op1 addDependency:op3];
 
    提示:在指定依赖关系时,注意不要循环依赖,否则不工作。
 
 
2.3. GCD,C语言                                                      
 
 GCD就是为了在“多核”上使用多线程技术
 
 1> 要使用GCD,所有的方法都是dispatch开头的
 2> 名词解释
 global  全局
 queue   队列
 async   异步
 sync    同步
 
 3> 要执行异步的任务,就在全局队列中执行即可
 dispatch_async 异步执行控制不住先后顺序
 
 4> 关于GCD的队列
全局队列    dispatch_get_global_queue
            参数:优先级 DISPATCH_QUEUE_PRIORITY_DEFAULT
                 始终是 0
     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
 dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"main - > %@", [NSThread currentThread]);
    });
 
     
            只能同歩
 5> 异步和同步与方法名无关,与运行所在的队列有关!
 同步主要用来控制方法的被调用的顺序
 


A、Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。

dispatch queue分成以下三种:

1)运行在主线程的Main queue,通过dispatch_get_main_queue获取。


/*!
* @function dispatch_get_main_queue
*
* @abstract
* Returns the default queue that is bound to the main thread.
*
* @discussion
* In order to invoke blocks submitted to the main queue, the application must
* call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
* thread.
*
* @result
* Returns the main queue. This queue is created automatically on behalf of
* the main thread before main() is called.
*/
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT struct dispatch_queue_s _dispatch_main_q;
#define dispatch_get_main_queue() \
DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q)

可以看出,dispatch_get_main_queue也是一种dispatch_queue_t。

2)并行队列global dispatch queue,通过dispatch_get_global_queue获取,由系统创建三个不同优先级的dispatch queue。并行队列的执行顺序与其加入队列的顺序相同。

3)串行队列serial queues一般用于按顺序同步访问,可创建任意数量的串行队列,各个串行队列之间是并发的。

当想要任务按照某一个特定的顺序执行时,串行队列是很有用的。串行队列在同一个时间只执行一个任务。我们可以使用串行队列代替锁去保护共享的数据。和锁不同,一个串行队列可以保证任务在一个可预知的顺序下执行。

serial queues通过dispatch_queue_create创建,可以使用函数dispatch_retain和dispatch_release去增加或者减少引用计数。


3、详解GCD
GCD的用法:
 //  后台执行:
 dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });

 // 主线程执行:
 dispatch_async(dispatch_get_main_queue(), ^{
      // something
 });

 // 一次性执行:
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
     // code to be executed once
 });

 // 延迟2秒执行:
 double delayInSeconds = 2.0;
 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
     // code to be executed on the main queue after delay
 });

 // 自定义dispatch_queue_t
 dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL);
 dispatch_async(urls_queue, ^{  
   // your code 
 });
 dispatch_release(urls_queue);

 // 合并汇总结果
 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程一
 });
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
      // 并行执行的线程二
 });
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
      // 汇总结果
 });

一个应用GCD的例子:


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
        NSError * error;
        NSString * data = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
        if (data != nil) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"call back, the data is: %@", data);
            });
        } else {
            NSLog(@"error when download:%@", error);
        }
    });
复制代码
GCD的另一个用处是可以让程序在后台较长久的运行。

在没有使用GCD时,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是在使用GCD后,app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。

让程序在后台长久运行的示例代码如下:


// AppDelegate.h文件
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundUpdateTask;

// AppDelegate.m文件
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [self beingBackgroundUpdateTask];
    // 在这里加上你需要长久运行的代码
    [self endBackgroundUpdateTask];
}

- (void)beingBackgroundUpdateTask
{
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [self endBackgroundUpdateTask];
    }];
}

- (void)endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
4、详解NSOperation 
使用 NSOperation 的方式有两种,
一种是用定义好的两个子类:
NSInvocationOperation 和 NSBlockOperation。
另一种是继承 NSOperation
如果你也熟悉 Java,NSOperation 就和 java.lang.Runnable 接口很相似。和 Java 的
Runnable 一样,NSOperation 也是设计用来扩展的,只需继承重写 NSOperation 的一个方 法 main。相当与 java 中 Runnalbe 的 Run 方法。然后把 NSOperation 子类的对象放入 NSOperationQueue 队列中,该队列就会启动并开始处理它。
NSInvocationOperation 例子:
和前面一篇博文一样,我们实现一个下载图片的例子。新建一个 Single View app,拖放一 个 ImageView 控件到 xib 界面。
实现代码如下:
 #import "ViewController.h"
#define kURL @"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"

@interface ViewController ()

@end
@implementation ViewController

 - (void)viewDidLoad {
[super viewDidLoad];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWit hTarget:self  selector:@selector(downloadImage:)
object:kURL];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
 // Do any additional setup after loading the view, typically from a nib. }
-(void)downloadImage:(NSString *)url{
 NSLog(@"url:%@", url);
NSURL *nsUrl = [NSURL URLWithString:url];
 NSData *data = [[NSData alloc]initWithContentsOfURL:nsUrl];
 UIImage * image = [[UIImage alloc]initWithData:data];
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
}
-(void)updateUI:(UIImage*) image{
 self.imageView.image = image;
 }
1. viewDidLoad 方法里可以看到我们用 NSInvocationOperation 建了一个后台线程,
并且放到 NSOperationQueue 中。后台线程执行 downloadImage 方法。 2. downloadImage 方法处理下载图片的逻辑。下载完成后用
performSelectorOnMainThread 执行主线程 updateUI 方法。 3. updateUI 并把下载的图片显示到图片控件中。
运行可以看到下载图片显示在界面上。
第二种方式继承 NSOperation
在.m 文件中实现 main 方法,main 方法编写要执行的代码即可。
如何控制线程池中的线程数?
队列里可以加入很多个 NSOperation, 可以把 NSOperationQueue 看作一个线程池,可往线
程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走 操作,并执行它。
通过下面的代码设置:
[queue setMaxConcurrentOperationCount:5]; 线程池中的线程数,也就是并发操作数。默认情况下是-1,-1 表示没有限制,这样会同时运
行队列中的全部的操作。
0 0
原创粉丝点击