IOS之线程
一、NSThread
1.线程阻塞
- (IBAction)btnClick {
NSThread *current = [NSThread currentThread];
for (int i = 0; i<20000; i++) {
NSLog(@"run---%@", current);
}
return NULL;
}
这样会阻塞当前主线程,即UI线程,导致界面死了
void *run(void *data) {
NSThread *current = [NSThread currentThread];
for (int i = 0; i<20000; i++) {
NSLog(@"run---%@", current);
}
return NULL;
}
- (IBAction)btnClick {
// 1.获得当前的线程
NSThread *current = [NSThread currentThread];
NSLog(@"btnClick---%@", current);
// 2.执行一些耗时操作 : 创建一条子线程
pthread_t threadId;
pthread_create(&threadId, NULL, run, NULL);
}
这样另外开启一个线程执行任务,就不会阻塞主线程了。
2.NSThread使用的三种方式
- (IBAction)btnClick {
// 1.获得当前的线程
NSThread *current = [NSThread currentThread];
NSLog(@"btnClick---%@", current);
// NSThread *main = [NSThread mainThread];
// NSLog(@"btnClick---%@", main);
// 2.执行一些耗时操作 : 创建一条子线程
[self threadCreate];
}
- (void)run:(NSString *)param
{
NSThread *current = [NSThread currentThread];
//for (int i = 0; i<10000; i++) {
NSLog(@"%@----run---%@", current, param);
//}
}
/**
* NSThread的创建方式
* 隐式创建线程, 并且直接(自动)启动
*/
- (void)threadCreate3
{
// 在后台线程中执行 === 在子线程中执行
[self performSelectorInBackground:@selector(run:) withObject:@"abc参数"];
}
/**
* NSThread的创建方式
* 创建完线程直接(自动)启动
*/
- (void)threadCreate2
{
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是参数"];
}
/**
* NSThread的创建方式
* 1> 先创建初始化线程
* 2> start开启线程
*/
- (void)threadCreate
{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
thread.name = @"线程A";
// 开启线程
[thread start];
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
thread2.name = @"线程B";
// 开启线程
[thread2 start];
}
3.线程状态
- (void)viewDidLoad
{
[super viewDidLoad];
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
self.thread.name = @"线程A";
}
- (void)test
{
NSLog(@"test - 开始 - %@", [NSThread currentThread].name);
// [NSThread sleepForTimeInterval:5]; // 阻塞状态
// NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5.0];
// [NSThread sleepUntilDate:date];
for (int i = 0; i<1000; i++) {
NSLog(@"test - %d - %@", i, [NSThread currentThread].name);
if (i == 50) {
// [NSThread exit];
}
}
NSLog(@"test - 结束 - %@", [NSThread currentThread].name);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 开启线程
[self.thread start];
}
4.线程同步
/** 剩余票数 */
@property (nonatomic, assign) int leftTicketsCount;
@property (nonatomic, strong) NSThread *thread0;
@property (nonatomic, strong) NSThread *thread1;
@property (nonatomic, strong) NSThread *thread2;
@end
@implementation HMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 默认有100张
self.leftTicketsCount = 100;
// 开启多条线程同时卖票
self.thread0 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread0.name = @"售票员 A";
// self.thread0.threadPriority = 0.0;
self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread1.name = @"售票员 B";
// self.thread1.threadPriority = 1.0;
self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
self.thread2.name = @"售票员 C";
// self.thread2.threadPriority = 0.0;
}
/**
* 卖票
*/
- (void)saleTicket
{
while (1) {
@synchronized(self) { // 加锁(只能用一把锁)
// 1.先检查票数
int count = self.leftTicketsCount;
if (count > 0) {
// 暂停
// [NSThread sleepForTimeInterval:0.0002];
// 2.票数 - 1
self.leftTicketsCount = count - 1;
NSThread *current = [NSThread currentThread];
NSLog(@"%@ 卖了一张票, 剩余%d张票", current.name, self.leftTicketsCount);
} else {
// 退出线程
[NSThread exit];
}
} // 解锁
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.thread0 start];
[self.thread1 start];
[self.thread2 start];
}
这里模拟四个窗口买票,多线程之间执行任务是异步的,故要让线程同步,这里通过加锁的方式实现线程同步,是数据准确。通过@synchronized{}实现加锁
5.线程之间进行通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 在子线程中调用download方法下载图片
[self performSelectorInBackground:@selector(download) withObject:nil];
}
/**
* 下载图片 : 子线程
*/
- (void)download
{
// 1.根据URL下载图片
NSURL *url = [NSURL URLWithString:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
NSLog(@"-------begin");
NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
NSLog(@"-------end");
UIImage *image = [UIImage imageWithData:data];
// 2.回到主线程显示图片
// [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
// setImage: 1s
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
// [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
}
/**
* 设置(显示)图片: 主线程
*/
//- (void)settingImage:(UIImage *)image
//{
// self.imageView.image = image
//}
二、GCD
1.基本使用
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelectorInBackground:@selector(test) withObject:nil];
// [self syncMainQueue];
}
- (void)test
{
NSLog(@"test --- %@", [NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"任务 --- %@", [NSThread currentThread]);
});
}
/**
* 使用dispatch_async异步函数, 在主线程中往主队列中添加任务
*/
- (void)asyncMainQueue
{
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
}
/**
* 使用dispatch_sync同步函数, 在主线程中往主队列中添加任务 : 任务无法往下执行
*/
- (void)syncMainQueue
{
// 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]);
// });
// 不会开启新的线程, 所有任务在主线程中执行
}
// 凡是函数名种带有create\copy\new\retain等字眼, 都需要在不需要使用这个数据的时候进行release
// GCD的数据类型在ARC环境下不需要再做release
// CF(Core Foundation)的数据类型在ARC环境下还是需要再做release
/**
* 用dispatch_sync同步函数往串行列中添加任务
*/
- (void)syncSerialQueue
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
// 2.添加任务到队列中 执行
dispatch_sync(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 3.释放资源
// dispatch_release(queue); // MRC(非ARC)
// 总结: 不会开启新的线程
}
/**
* 用dispatch_sync同步函数往并发队列中添加任务
*/
- (void)syncGlobalQueue
{
// 1.获得全局的并发队列
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]);
});
// 总结: 不会开启新的线程, 并发队列失去了并发的功能
}
/**
* 用dispatch_async异步函数往串行队列中添加任务
*/
- (void)asyncSerialQueue
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 总结: 只开1个线程执行任务
}
/**
* 用dispatch_async异步函数往并发队列中添加任务
*/
- (void)asyncGlobalQueue
{
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.添加任务到队列中 执行
dispatch_async(queue, ^{
NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
});
// 总结: 同时开启了3个线程
}
//主队列
dispatch_queue_t queue = dispatch_get_main_queue();
同步函数dispatch_sync 主队列中添加任务 开启一个线程执行任务
异步函数dispatch_async 主队列中添加任务 不会开启新的线程, 所有任务在主线程中执行
//创建的串行队列
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
//全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
同步函数dispatch_sync 串行队列中添加任务 不会开启新的线程
同步函数dispatch_sync 全局的并发队列中添加任务 不会开启新的线程, 并发队列失去了并发的功能
异步函数dispatch_async 串行队列中添加任务 只开1个线程执行任务
异步函数dispatch_async 全局的并发队列中添加任务 有几个任务开启几个线程
2.GCD下载图片
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"--download--%@", [NSThread currentThread]);
// 下载图片
NSURL *url = [NSURL URLWithString:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
UIImage *image = [UIImage imageWithData:data];
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"--imageView--%@", [NSThread currentThread]);
self.imageView.image = image;
});
});
}
3.其他用法(下载两张图片合并为一张)
@interface HMViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView1;
@property (weak, nonatomic) IBOutlet UIImageView *imageView2;
@property (weak, nonatomic) IBOutlet UIImageView *bigImageView;
@end
@implementation HMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 图片1: http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg
// 图片2: http://news.baidu.com/z/resource/r/image/2014-06-22/b2a9cfc88b7a56cfa59b8d09208fa1fb.jpg
/**
1.下载图片1和图片2
2.将图片1和图片2合并成一张图片后显示到imageView上
思考:
* 下载图片 : 子线程
* 等2张图片都下载完毕后, 才回到主线程
*/
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 创建一个组
dispatch_group_t group = dispatch_group_create();
// 开启一个任务下载图片1
__block UIImage *image1 = nil;
dispatch_group_async(group, global_queue, ^{
image1 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
});
// 开启一个任务下载图片2
__block UIImage *image2 = nil;
dispatch_group_async(group, global_queue, ^{
image2 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/b2a9cfc88b7a56cfa59b8d09208fa1fb.jpg"];
});
// 同时执行下载图片1\下载图片2操作
// 等group中的所有任务都执行完毕, 再回到主线程执行其他操作
dispatch_group_notify(group, main_queue, ^{
self.imageView1.image = image1;
self.imageView2.image = image2;
// 合并
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
[image1 drawInRect:CGRectMake(0, 0, 100, 100)];
[image2 drawInRect:CGRectMake(100, 0, 100, 100)];
self.bigImageView.image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
});
// if (self.log == NO) {
// NSLog(@"-------touchesBegan");
// self.log = YES;
// }
// static dispatch_once_t onceToken;
// dispatch_once(&onceToken, ^{
// NSLog(@"-------touchesBegan");
// });
}
- (void)downlaod2image
{
dispatch_async(global_queue, ^{
NSLog(@"下载图片---%@", [NSThread currentThread]);
// 下载图片1
UIImage *image1 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
NSLog(@"下载完图片1---%@", [NSThread currentThread]);
// 下载图片2
UIImage *image2 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
NSLog(@"下载完图片2---%@", [NSThread currentThread]);
dispatch_async(main_queue, ^{
NSLog(@"显示图片---%@", [NSThread currentThread]);
self.imageView1.image = image1;
self.imageView2.image = image2;
// 合并
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
[image1 drawInRect:CGRectMake(0, 0, 100, 100)];
[image2 drawInRect:CGRectMake(100, 0, 100, 100)];
self.bigImageView.image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
});
});
}
- (UIImage *)imageWithURL:(NSString *)urlStr
{
NSURL *url = [NSURL URLWithString:urlStr];
NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
return [UIImage imageWithData:data];
}
- (void)delay
{
// NSLog(@"----touchesBegan----%@", [NSThread currentThread]);
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// });
// 1.全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.计算任务执行的时间
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
// 3.会在when这个时间点, 执行queue中的任务
dispatch_after(when, queue, ^{
NSLog(@"----run----%@", [NSThread currentThread]);
});
}
//- (void)run
//{
// NSLog(@"----run----%@", [NSThread currentThread]);
//}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。