多线程的简单使用

时间:2021-01-03 18:35:16

一、GCD、NSOperation、NSThread多线程

  1. GCD(Grand Central Dispatch)
    • 苹果开发的一个多核编程的解决方案,可以提高代码的执行效率与多核的利用率。
  2. NSOperation
    • NSOperation是面向对象的。
    • 不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上
    • 相关的类是NSOperation,NSOperationQueue。
    • NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
  3. NSThread
    • 缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。(NSThread的使用(本已过时)

二、GCD的简单使用

  • global 全局 | queue 队列 | async 异步 | sync 同步
 dispatch_get_global_queue
dispatch_get_main_queue
  • 全局队列 dispatch_get_global_queue 可同步 可异步
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]);
});
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
dispatch_apply执行某个代码片段N次。
dispatch_apply(5, globalQ, ^(size_t index) {
// 执行5次
}
);

* TouchesBegan

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

// 主线程调用耗时操作(阻塞主线程,执行期间不能操作界面)
//[self longTimeOperation];

// [self CGD_Demo1];
// [self CGD_Demo2];
// [self CGD_Demo3];
// [self GCDImage_Demo];

[self NSOperation_Demo1];
// [self NSOperation_Demo2];
// [self NSOperation_Demo3];

}
//GCD_Demo调用方法
- (void)longTimeOperation{

// 耗时操作
for (NSInteger index = 0; index < 20; index++) {

NSLog(@"耗时操作:%ld--%@",index,[NSThread currentThread]);
if (index == 10) {
NSLog(@"暂停,休息5秒");
[NSThread sleepForTimeInterval:3];
}
}
// 获得数据,返回主线程,更新UI
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
}

// 更新UI
- (void)updateUI{
for (NSInteger index = 0; index < 5; index++) {
NSLog(@"更新UI->%ld---当前线程->%@",index,[NSThread currentThread]);
}

}

* GCD_Demo

-(void)demo1{
// 创建队列 (串行)
// DISPATCH_QUEUE_SERIAL 串行: 特点: 任务顺序执行
// DISPATCH_QUEUE_CONCURRENT 并行 特点: 无序的
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
// 添加任务
dispatch_async(queue, ^{
NSLog(@"任务1,%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"任务2");
});

});
dispatch_async(queue, ^{
NSLog(@"任务3");
});
dispatch_async(queue, ^{
NSLog(@"任务4");
});
dispatch_async(queue, ^{
NSLog(@"任务5");
});
[self loadData];

}
- (void)loadData
{
// 回到主线程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"任务6---刷新UI");
NSLog(@"current1 = %@, mainThread = %@", [NSThread currentThread], [NSThread mainThread]);
});
}
 - (void)CGD_Demo2{

/** 异步执行,开辟新线程
* dispatch_queue_t queue 队列,在哪儿执行
* block 任务
*/


// 先获得全局队列(并发队列)
dispatch_queue_t globleQ = dispatch_get_global_queue(0, 0);
dispatch_async( globleQ , ^{
// 耗时操作
[self longTimeOperation];

// 回到主线程,更新UI
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUI];
});
});
}
// 调度组
// 例子:下载应用A\B,完成后并通知用户
- (void)CGD_Demo3{

// 创建组队列
dispatch_group_t group = dispatch_group_create();

// 创建任务1:
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"10秒之后,开始下载应用A-->当前线程:%@",[NSThread currentThread]);

});
// 创建任务2:
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2秒后,开始下载应用B-->当前线程:%@",[NSThread currentThread]);

});

// 线程完成后,通知更新UI
// 用于监听所有任务的执行情况 [所以此功能代码必须放在所有任务之后进行书写]
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"下载完成-->当前线程:%@",[NSThread currentThread]);

// 更新UI
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUI];
});
});
}
  • Mark 一 使用GCD 开辟子线程请求数据,然后回到主线程刷新数据(展示图片)
// 
-(void)GCDImage_Demo{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.imageView];

// 自己创建的
// dispatch_async(dispatch_queue_create(@"myQueue", DISPATCH_QUEUE_CONCURRENT), <#^(void)block#>)


// 系统方式
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_async(queue, ^{

// ======处理数据库=======

// 创建URL===
NSURL *url = [NSURL URLWithString:@"http://img2.3lian.com/2014/f6/127/d/39.jpg"];
// 转换成数据
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
// 初始化
UIImage *image = [[UIImage alloc] initWithData:data];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
// =====刷新界面======
self.imageView.image = image;
});
}
NSLog(@"current1 = %@, mainThread = %@", [NSThread currentThread], [NSThread mainThread]);
});
}

三、NSOperation的简单使用

  • NSOperation的常用方法

【1】、start:开始方法,当把NSOperation添加到NSOperationQueue中去后,队列会在操作中调用start方法。
【2】、addDependency,removeDependency:添加从属性,删除从属性,比如说有线程a,b,如果操作a从属于b,那么a会等到b结束后才开始执行。
【3】、setQueuePriority:设置线程的优先级。
例:[a setQueuePriority:NSOperationQueuePriorityVeryLow];一共有四个优先级:
NSOperationQueuePriorityLow,
NSOperationQueuePriorityNormal,
NSOperationQueuePriorityHigh,
NSOperationQueuePriorityVeryHigh。
当你添加一个操作到一个队列时,在对操作调用start之前,NSOperationQueue会浏览所有的操作,具有较高优先级的操作会优先执行,具有相同优先级的操作会按照添加到队列中顺序执行。
【4】、setCompletionBlock:设置回调方法,当操作结束后,会调用设置的回调block。这个block会在主线程中执行。

* NSOperation_Demo

-(void)NSOperation_Demo1{
// NSOperation不能直接进行多线程的创建,需要借助 :NSOperationQueue

// 使用NSOperation的第一个子类去创建子线程 :NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];

// 在单独使用子NSOperation的子类去创建线程的时候,一定要启动
[operation start];
// 在使用NSOperation的子类去创建线程的时候,实际上线程没有真正意义上的创建

// 使用NSOperation的第二个子类创建线程
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"我是block");
NSLog(@"currentThread %@", [NSThread currentThread]);
NSLog(@"%@", [NSThread mainThread]);

}];

// 只有启动的时候才会调用block中的内容
[blockOperation start];

// 需要把上面的两个线程,放到操作队列里
// addOperation一旦创建的对象加入到操作对列中,就不能调用start,否则会崩溃
// NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// [queue addOperation:operation];
// [queue addOperation:blockOperation];
}
- (void)test
{
NSLog(@"test...");
NSLog(@"currentThread == %@", [NSThread currentThread]);
NSLog(@"mainThread == %@", [NSThread mainThread]);
}
-(void)NSOperation_Demo2{
// 封装操作任务
NSBlockOperation *operation_A = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载应用_Zip");
}];
NSBlockOperation *operation_B = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"解压zip——应用");
}];

NSBlockOperation *UI_Operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"更新UI");
}];


// 将任务添加到队列
NSOperationQueue *globleQueue = [[NSOperationQueue alloc]init];//直接alloc/init 就是全局队列

// 添加依赖关系,必须在将任务添加到队列之前(必须先将应用下载完毕后,在解压缩)
[operation_B addDependency:operation_A];
[UI_Operation addDependency:operation_B];

// 添加任务到队列(->下载完成)
// waitUntilFinished : YES 等待执行完成后再继续
[globleQueue addOperations:@[operation_A,operation_B] waitUntilFinished:YES];

// 更新UI
[[NSOperationQueue mainQueue] addOperation:UI_Operation];

}
-(void)NSOperation_Demo3{
// 创建队列的对象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 最大的并发数量值
// 当值设置为1的时候,可以叫做串行: 即顺序执行

// 当值设置大于1的时候,叫做并行: 多条通道同时进行各自的任务
queue.maxConcurrentOperationCount = 2;

for (int i = 0; i < 10; i++) {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"currentThread == %@", [NSThread currentThread]);
NSLog(@"mainThread == %@", [NSThread mainThread]);
NSLog(@"===%d", i);

}];
[queue addOperation:blockOperation];
}

}

四、NSOprationQueue 与 GCD 的区别与选用容