多线程(归纳,总结)

总结一下多线程的东西,防止忘记。。。不然每次都得翻出来重新看

基础简介

  • 进程

  • 进程是指在系统中正在运行的一个应用程序

  • 每个进程之间是独立的,每个进程均运行在某专用且受保护的内存空间内

  • 线程

  • 1个进程要想执行任务,必须得有线程存在(每一个进程至少有一个线程)

  • 1个进程(程序)的所有任务都在线程里完成

多线程(归纳,总结)_第1张图片
Snip20160321_1.png

线程的执行是串行的,一个一个按顺序执行
所以一般优化程序流畅度,就要开启多个线程,多个线程之间是并行的,可以分开处理不同的操作,一起执行

  • 多线程的原理

  • 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)

  • 多线程并发(同时)执行,其实是CPU快速地在多线程之间调度(切换)

  • 线程不能太多

  • 如果线程过多,CPU调度不过来,会卡死

  • 每条线程都会执行缓慢

  • 多线程的优点

  • 能适当提高程序的执行效率

  • 能适当的提高资源利用率(CPU,内存的利用率,不会空转)

  • 多线程的缺点

  • 创建线程是有开销的,iOS主要成本包括:

    • 内核数据结构(大约1KB)
    • 栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K,不建议自己设定,系统既然设定了就是有用的),创建线程大约需要90毫秒的创建时间
    • 线程开启太多,会降低程序的性能,iOS一般最多5条线程
    • 线程越多,CPU在调度线程上的开销越大
    • 程序设计更加复杂:比如线程之间的通信,多线程的数据共享
  • 主线程

  • 一个程序运行后,默认会开启一条线程,成为"主线程"或者"UI线程"

  • 主线程的作用

  • 显示\刷新UI界面

  • 处理UI时间(比如点击事件\滚动事件\拖拽事件等)

  • 主线程的使用注意

  • 耗时操作放在子线程,不要放在主线程

四中多线程的实现

多线程(归纳,总结)_第2张图片
Snip20160321_2.png

pthead

纯C语言的,这玩意儿基本上不用
创建代码

// 第一个传一个
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一个参数需要创建pthread_t对象地址,第三个参数传一个C语言函数,第二个和第四个参数传NULL

// 例子
    pthread_t thread2;
    pthread_create(&thread2, NULL, buttonClick, NULL);

void * buttonClick(void *param)
{
    for (NSInteger i = 0; i<10000; i++) {
        NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
    }
    return NULL;
}

这个C的东西,反正我没用过...

NSThread

这种多线程的方法是比较轻量级,全程自己手动管理生命周期,同步,加锁等问题,封装了C语言的多线程

  • 动态方法
/**
* selector : 线程执行的方法,这个selector最多只能接收一个参数
* target : selector是消息发送的对象,发给谁的
* argument : 传给selector的唯一参数
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;

// 例子,线程的开启
// 初始化线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 设置线程的优先级(0.0 ~ 1.0,1.0是最高级)
thread.threadPriority = 1;
// 设置线程的名字
thread.name = @"jack";
// 开启线程
[thread start];
  • 其他创建多线程的方法
  • 静态方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 例子
// 调用完毕后,会马上创建并开启新线程  
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  
  • 隐式创建线程
[self performSelectorInBackground:@selector(run) withObject:nil]

优点: 简单快捷
缺点:无法对线程进行更详细的设置

相关方法

+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程

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

线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
控制线程的状态
  • 启动线程
// 进入就绪状态->运行状态-- 执行完毕 -- > 死亡状态
- (void)start;
  • 阻塞(暂停)线程
  • (void)sleepUntilDate:(NSDate *)date;
// 例子
//    [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]从现在开始到永远
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
  • (void)sleepForTimeInterval:(NSTimeInterval)ti;
    [NSThread sleepForTimeInterval:2]; // 让线程睡眠2秒(阻塞2秒)
  • 强制死亡状态
// 强制停止线程
+ (void)exit;

一旦线程死亡,就必须重新开启新的线程,不能重新调用

线程锁

在多个线程同时访问一个对象的时候,同时对该对象进行了操作,就会出现安全隐患,用NSThread的方法需要自己管理线程锁,这就是互斥锁

  • 互斥锁的使用格式
    @synchronized(锁对象) { // 需要锁定的代码 }
    // 一份代码只有一把锁,多把锁是无效的,还是乱的
    // 这个锁一般是全局变量,表示唯一的(切记)

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

加互斥锁也就是要打到"线程同步"的问题
线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
互斥锁,就是使用了线程同步技术
意思就是说所有线程上同一对象是一样的!!!!

线程之间的通信

在一个进程中,多个线程之间是有一定关联,相互通信的

  • 一般在子线程进行复杂操作,在主线程刷新UI,这就是线程通信
多线程(归纳,总结)_第3张图片
Snip20160321_5.png

常用方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 最后这个(BOOL)wait是指下面代码是否等这个执行完了之后再进行
  • 苹果还提供了第二种线程同步的方法NSMachPort
    NSPort有3个子类:NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
    使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。
    可以看到,使用NSMachPort的结果为调用了其它线程的1个函数,而这正是performSelector所做的事情,所以,NSMachPort是个鸡肋。线程间通信应该都通过performSelector来搞定。

GCD

Grand Central Dispatch(中央调度)
纯C语言,对NSThread进行了封装

切记:

不要在当前穿行队列里使用sync函数,因为会卡住线程,调用sync函数,系统会马上去执行这个函数,但是当前线程也是串行的,他会默认执行完当前线程再去执行别的,这就造成了冲突,会卡死

GCD优势:

  • 为多核的并发运算提出的解决方法
  • 自动利用更多的CPU内核(比如双核,四核)
  • 自动管理线程的生命周期(创建线程,调度任务,销毁线程等)
  • 只需要告诉GCD执行什么,GCD会自行管理任务

GCD两个核心概念

  • 任务:执行什么操作
  • 队列:用来存放任务

GCD的使用就2个步骤

  • 定制任务
  • 确定想做的事情

将任务添加到队列中

  • GCD会自动将队列中的任务取出,放到对应的线程中执行
  • 任务的取出遵循队列的FIFO原则:先进先出,后进后出

GCD的OC对象的内存管理
GCD支持Cocoa内存管理机制,因此可以在提交到queue的block中自由地使用Objective-C对象。每个dispatch queue维护自己的autorelease pool确保释放autorelease对象,但是queue不保证这些对象实际释放的时间。如果应用消耗大量内存,并且创建大量autorelease对象,你需要创建自己的autorelease pool,用来及时地释放不再使用的对象。

同步和异步的区别

  • 同步:只能在当前线程中执行任务,不具备开启线程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 异步:可以在新的线程中执行任务,具备开启线程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
多线程(归纳,总结)_第4张图片
Snip20160321_7.png

例子

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self syncConcurrent];
}

/**
 * 同步函数 + 主队列:
 */
- (void)syncMain
{
    NSLog(@"syncMain ----- begin");
    
    // 1.获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncMain ----- end");
}

/**
 * 异步函数 + 主队列:只在主线程中执行任务
 */
- (void)asyncMain
{
    // 1.获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
 */
// 俩参数,第一个是名字,第二个是执行的优先级
/**
* 全局并发队列的优先级
* #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 // 后台
*/
- (void)syncSerial
{
    // 1.创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
 */
- (void)asyncSerial
{
    // 1.创建串行队列
    // 两个参数,第一个是名字,第二个是穿行队列
    // DISPATCH_QUEUE_CONCURRENT 并行队列
    // DISPATCH_QUEUE_SERIAL            串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
//    默认是穿行队列,所以第二个参数传NULL也行
![Uploading Snip20160321_7_826971.png . . .]
//    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函数 + 并发队列:不会开启新的线程
 * 因为还是同步函数,所以你的并发执行并没有什么卵用
 */
- (void)syncConcurrent
{
    // 1.获得全局的并发队列
    // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
    // 俩参数,第一个为线程的优先级,第二个为预留参数,传0就行了
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncConcurrent--------end");
}

/**
 * 异步函数 + 并发队列:可以同时开启多条线程
 */
- (void)asyncConcurrent
{
    // 1.创建一个并发队列
    // label : 相当于队列的名字
    // 俩参数,第一个是线程的名字,第二个是优先级
//    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.获得全局的并发队列
    // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"2-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"3-----%@", [NSThread currentThread]);
        }
    });
    
    NSLog(@"asyncConcurrent--------end");
//    dispatch_release(queue);
}

GCD里面线程之间的通信

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主线程  (通信)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}
GCD的其他使用
  • barrier障碍

把几个线程分开,先执行障碍之前的代码,然后执行障碍,最后执行障碍以后的代码

    dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)

例子

- (void)barrier
{
//    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    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_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}
  • 延迟执行

dispatch_time延迟执行的代码在block里面,第一个参数为开始时间,第二个参数为延迟的秒数,单位为纳米,转化单位NSEC_PER_SEC ,第三个参数为执行的线程

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        <#code to be executed after a specified delay#>
    });

例子

- (void)delay
{
    NSLog(@"touchesBegan-----");
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run-----");
    });
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}

- (void)run
{
    NSLog(@"run-----");
}
  • 一次性代码(单例要用)
// 一次执行代码,这个代码在这一整个进程里就执行一次,不会执行第二次
- (void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------run");
    });
}
  • 快速迭代(我感觉就是遍历)
// 用GCD
- (void)apply
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSString *from = @"/Users/xiaomage/Desktop/From";
    NSString *to = @"/Users/xiaomage/Desktop/To";
    
    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subpaths = [mgr subpathsAtPath:from];

    // 快速迭代  block里面有个参数为size_t index
    dispatch_apply(subpaths.count, queue, ^(size_t index) {
        NSString *subpath = subpaths[index];
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
        // 剪切
        [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
        
        NSLog(@"%@---%@", [NSThread currentThread], subpath);
    });
}

/**
 * 对比:传统文件剪切
 */
- (void)moveFile
{
    NSString *from = @"/Users/xiaomage/Desktop/From";
    NSString *to = @"/Users/xiaomage/Desktop/To";

    NSFileManager *mgr = [NSFileManager defaultManager];
    NSArray *subpaths = [mgr subpathsAtPath:from];

    for (NSString *subpath in subpaths) {
        NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
        NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 复杂耗时操作放在子线程       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 剪切
            [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
        });
    }
}
  • 设置依赖分组关系
  • 创建队列
  • 创建group组对象dispatch_group_create()
  • 讲操作加到组里
  • 在组里完成所有操作后,通过 dispatch_notify(<#object#>, <#queue#>, <#notification_block#>) 方法执行下一步操作,有三个参数,第一个是哪一组的,第二个是在哪个队列,第三个是要执行的方法block
- (void)group
{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 创建一个队列组
    dispatch_group_t group = dispatch_group_create();
    
    // 1.下载图片1
    dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        self.image1 = [UIImage imageWithData:data];
    });
    
    // 2.下载图片2
    dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        self.image2 = [UIImage imageWithData:data];
    });
    
    // 3.将图片1、图片2合成一张新的图片
    dispatch_group_notify(group, queue, ^{
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 绘制图片
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
            // 4.将新图片显示出来 
            self.imageView.image = image;
        });
    });
}

NSOperation

NSOperation的作用:配合使用NSOperation和NSOperationQueue也能实现多线程编程

NSOperation和NSOperationQueue实现多线程的具体步骤

  1. 先将需要执行的操作封装到一个NSOperation对象中
  2. 然后将NSOperation对象添加到NSOperationQueue中
  3. 系统会自动将NSOperationQueue中的NSOperation取出来
  4. 将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

使用NSOperation子类的方式有3种

  • NSInvocationOperation(不常用)
  • NSBlockOperation
  • 自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
- (void)invocationOperation
{
    // 创建NSInvocationOperation对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 调用start方法开始执行操作
    [op start];
}
  • 默认情况下,调用start方法后并不会开一条新线程去执行操作,而是在当前线程同步进行操作
  • 只有讲NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSBlockOperation

在NSBlockOperation操作里,只要封装的任务个数大于1,就会异步执行

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    }];
    
    // 添加额外的任务(在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
    }];

    [op addExecutionBlock:^{
        NSLog(@"下载3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下载4------%@", [NSThread currentThread]);
    }];
    
    [op start];
}
NSOperationQueue

NSOperation一般是和NSOperationQueue配合使用的
在NSOperation调用start方法的时候,如果没有加入NSOperationQueue,那么他们是同步在当前线程执行的,只有加入了NSOperationQueue,系统会自动给你异步执行,而且怎么异步,开多少线程都不用你管

两种方式

  • (void)addOperation:(NSOperation *)op;
  • (void)addOperationWithBlock:(void (^)(void))block;

示例如下
```objc
- (void)operationQueue1
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作(任务)
    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 创建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 创建XMGOperation
    XMGOperation *op5 = [[XMGOperation alloc] init];
    
    // 添加任务到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
    [queue addOperation:op5]; // [op5 start]
}

- (void)operationQueue2
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作
    //    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"download1 --- %@", [NSThread currentThread]);
    //    }];
    
    // 添加操作到队列中
    //    [queue addOperation:op1];
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
  • 最大并发数
    可以设置最大并发数,也就是最多开启的线程数
    把最大并发数设置成1,就变成了串行
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
  • 取消队列的所有操作
- (void)cancelAllOperations;```
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作

- 暂停和恢复队列

```objc    
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
  • 线程之间的依赖
    这个设置起来比着GCD要简单的多,GCD可以通过barrier,建组的方式,然而,NSOperation却只需要一行代码,而且还可以设置不同线程之间的依赖等
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    op5.completionBlock = ^{
        NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
    };
    
    // 设置依赖
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
}

同样是下载图片的依赖关系

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
// 想让block里面改外面的局部变量的值,在外部参数加__block
    __block UIImage *image1 = nil;
    // 下载图片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image1 = [UIImage imageWithData:data];
    }];
    
    __block UIImage *image2 = nil;
    // 下载图片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];

        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image2 = [UIImage imageWithData:data];
    }];
    
    // 合成图片
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 绘制图片
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        image1 = nil;
        
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        image2 = nil;
        
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];
}
  • 线程之间的通讯
- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 图片的网络路径
       NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
     
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}

你可能感兴趣的:(多线程(归纳,总结))