标签:
一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。
方法 | 功能 |
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument | 初始化一个子线程,但需要手动开启 |
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument | 初始化一个子线程并自动开启 |
-start | 开启子线程 |
-cancel | 取消当前子线程,不是真正的取消,而是给子线程发送了一个“取消”消息,标记为canceled |
+exit |
直接将线程退出 |
1 - (void)viewDidLoad { 2 #pragma mark - NSThread手动开辟子线程 3 // 参数1: target 4 // 参数2: 方法 5 // 参数3: 要传的参数 6 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHello:) object:@"说你好!"]; 7 #pragma mark - 启动子线程 8 [thread start]; 9 10 // 判断一个线程是否正在执行 11 NSLog(@"是否正在执行%d",[thread isExecuting]); 12 // 判断一个线程是否完成了任务(是否执行完毕) 13 NSLog(@"是否执行完毕%d", [thread isFinished]); 14 15 #pragma mark - 释放子线程的两种方式 16 // 第一种:取消线程,不是真正的取消,而是给子线程发送了一个“取消”消息,标记为canceled 17 // 判断线城是否被标记为canceled 18 // NSLog(@"%d", [thread isCancelled]); 19 // [thread cancel]; 20 // // 第二种:直接将线程退出 21 // [NSThread exit]; 22 23 } 24 25 - (void)sayHello:(NSString *)hello { 26 NSLog(@"%@", hello); 27 // 打印当前线程 28 NSLog(@"当前线程:%@", [NSThread currentThread]); 29 NSLog(@"主线程:%@", [NSThread mainThread]); 30 }
第二种:
1 - (void)viewDidLoad { 2 #pragma mark - NSThread自动开辟子线程 3 // 线程自动开启,不需要创建NSThread的对象 4 // 与手动开启线程的方法相比,target 和 selector 两个参数顺序颠倒 5 [NSThread detachNewThreadSelector:@selector(sayHello:) toTarget:self withObject:@"说你好!"]; 6 } 7 8 - (void)sayHello:(NSString *)hello { 9 NSLog(@"%@", hello); 10 // 打印当前线程 11 NSLog(@"当前线程:%@", [NSThread currentThread]); 12 NSLog(@"主线程:%@", [NSThread mainThread]); 13 }
1 - (void)viewDidLoad { 2 #pragma mark - NSObject开辟子线程 3 /** 4 * 开启线程的方式之二:NSObject 5 */ 6 // 使用performSelectorInBackground开辟子线程 7 // 参数1:方法 8 // 参数2:要传的参数 9 [self performSelectorInBackground:@selector(sayHello:) withObject:@"说你好!"]; 10 self.view.backgroundColor = [UIColor yellowColor]; 11 12 } 13 14 - (void)sayHello:(NSString *)hello { 15 NSLog(@"%@", hello); 16 // 打印当前线程 17 NSLog(@"当前线程:%@", [NSThread currentThread]); 18 NSLog(@"主线程:%@", [NSThread mainThread]); 19 20 // 回到主线程修改view的背景颜色 21 // 参数1:方法 22 // 参数2:要传的参数 23 // 参数3:是否等待子线程完成后再进入主线程 24 [self performSelectorOnMainThread:@selector(changeColor) withObject:nil waitUntilDone:NO]; 25 } 26 27 - (void)changeColor { 28 self.view.backgroundColor = [UIColor cyanColor]; 29 // 打印当前线程 30 NSLog(@"==当前线程:%@", [NSThread currentThread]); 31 NSLog(@"==主线程:%@", [NSThread mainThread]); 32 }
开辟子线程成功
1 @interface ViewController () 2 /// 声明一个ImageView用来显示图片 3 @property (nonatomic, strong) UIImageView *showImageView; 4 /// 声明一个NSData类型的数据用于接收图片的数据 5 @property (nonatomic, strong) NSData *imageData; 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad { 11 [super viewDidLoad]; 12 // 创建imageView 13 _showImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, 414, 300)]; 14 _showImageView.backgroundColor = [UIColor greenColor]; 15 [self.view addSubview:_showImageView]; 16 17 18 } 19 20 #pragma mark - 触发子线程去加载数据 21 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { 22 [self performSelectorInBackground:@selector(loadImageData) withObject:nil]; 23 } 24 25 // 加载图片数据 26 - (void)loadImageData { 27 NSLog(@"当前线程:%@", [NSThread currentThread]); 28 NSLog(@"主线程:%@", [NSThread mainThread]); 29 NSURL *url = [NSURL URLWithString:@"http://img4q.duitang.com/uploads/item/201506/13/20150613185209_nHy5E.jpeg"]; 30 NSURLRequest *request = [NSURLRequest requestWithURL:url]; 31 self.imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; 32 // 创建主线程用于刷新UI 33 [self performSelectorOnMainThread:@selector(renovateUI) withObject:nil waitUntilDone:YES]; 34 35 } 36 37 // 主线程刷新UI 38 - (void)renovateUI { 39 // 安全判断 40 if ([NSThread mainThread]) { 41 _showImageView.image = [UIImage imageWithData:self.imageData]; 42 } 43 } 44 @end
点击触发事件,开辟子线程成功,在主线程中刷新UI,图片显示成功
1 - (void)viewDidLoad { 2 /** 3 * NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue。 4 NSOperation的子类本身和多线程没有任何关系,它只是封装了一定的代码段和数据去实现一个功能。 5 在单独使用NSOperation的子类去创建线程的时候,实际上线程没有真正被创建。 6 */ 7 // 使用NSOperation的第一个子类去创建子线程 -- NSInvocationOperation 8 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sayHello:) object:@"??"]; 9 // 在单独使用NSOperation的子类去创建线程的时候,一定要启动 10 [operation start]; 11 12 } 13 14 - (void)sayHello:(NSString *)str { 15 NSLog(@"%@", str); 16 // 此时发现两个线程地址相同,也就是当前线程就是主线程,子线程没有创建成功 17 // 18 NSLog(@"%@", [NSThread currentThread]); 19 NSLog(@"%@", [NSThread mainThread]); 20 }
没有开辟子线程
1 - (void)viewDidLoad { 2 /** 3 * NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue。 4 NSOperation的子类本身和多线程没有任何关系,它只是封装了一定的代码段和数据去实现一个功能。 5 在单独使用NSOperation的子类去创建线程的时候,实际上线程没有真正被创建。 6 */ 7 // 使用NSOperation的第二个子类去创建子线程 -- NSBlockOperation 8 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 9 NSLog(@"我是Block"); 10 // 此时发现两个线程地址相同,也就是当前线程就是主线程,子线程没有创建成功 11 // 12 NSLog(@"++++++%@", [NSThread currentThread]); 13 NSLog(@"++++++%@", [NSThread mainThread]); 14 }]; 15 // 在单独使用NSOperation的子类去创建线程的时候,一定要启动 16 [blockOperation start]; 17 }
没有开辟子线程
注意:一旦将创建完成的线程放到队列中,就不能再start启动,否则程序会crush
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 /** 4 * NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue。 5 NSOperation的子类本身和多线程没有任何关系,它只是封装了一定的代码段和数据去实现一个功能。 6 在单独使用NSOperation的子类去创建线程的时候,实际上线程没有真正被创建。 7 */ 8 // 创建操作队列 9 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 10 // 最大并发数量 11 // 当值设置为1的时候,可以叫做串行:即顺序执行 12 // 当值设置大于1的时候,叫做并行:多条通道同时进行各自的任务 13 queue.maxConcurrentOperationCount = 2; 14 for (int i = 0; i < 10; i++) { 15 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 16 NSLog(@"%@, %@, %d", [NSThread currentThread], [NSThread mainThread], i); 17 }]; 18 #warning - 注意:一旦将创建完成的线程放到队列中,就不能再start启动,否则程序会crush 19 [queue addOperation:blockOperation]; 20 } 21 }
开辟子线程成功
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 使用GCD去创建串行队列 4 // 第一种:系统提供的创建串行队列的方法 5 // 使用主队列(跟主线程相关联的队列) 6 // 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行 7 dispatch_queue_t queue = dispatch_get_main_queue(); 8 // 往队列里面添加任务 9 dispatch_async(queue, ^{ 10 NSLog(@"这是第一个任务,当前线程是:%@, 是否主线程 :%d ", [NSThread currentThread], [[NSThread currentThread] isMainThread]); 11 }); 12 13 }
所有任务都在主线程中进行
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 使用GCD创建并行队列 4 // 第一种方式 5 // global queue GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建 6 // 优先级 7 // DISPATCH_QUEUE_PRIORITY_DEFAUL, 默认(中) 8 // DISPATCH_QUEUE_PRIORITY_HIGH, 高 9 // DISPATCH_QUEUE_PRIORITY_LOW, 低 10 // DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台 11 // 第一个参数就是队列的优先级,第二个参数是苹果预留的参数为了以后去使用,目前没有用到,填写0 12 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 13 #pragma mark - 向队列中添加任务 14 dispatch_async(queue, ^{ 15 NSLog(@"current1%@, main1%@", [NSThread currentThread], [NSThread mainThread]); 16 }); 17 dispatch_async(queue, ^{ 18 NSLog(@"current2%@, main2%@", [NSThread currentThread], [NSThread mainThread]); 19 }); 20 dispatch_async(queue, ^{ 21 NSLog(@"current3%@, main3%@", [NSThread currentThread], [NSThread mainThread]); 22 }); 23 dispatch_async(queue, ^{ 24 NSLog(@"current4%@, main4%@", [NSThread currentThread], [NSThread mainThread]); 25 }); 26 dispatch_async(queue, ^{ 27 NSLog(@"current5%@, main5%@", [NSThread currentThread], [NSThread mainThread]); 28 }); 29 }
开辟子线程成功,子线程并发执行
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 第二种创建串行队列的方式 4 // 参数1:系统的保留字段,队列名 5 // 参数2:系统提供的宏 6 // 两个参数的位置可以互换,没有严格限制 7 dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL); 8 9 #pragma mark - 向队列中添加任务 10 dispatch_async(queue, ^{ 11 NSLog(@"current1%@, main1%@", [NSThread currentThread], [NSThread mainThread]); 12 }); 13 dispatch_async(queue, ^{ 14 NSLog(@"current2%@, main2%@", [NSThread currentThread], [NSThread mainThread]); 15 }); 16 dispatch_async(queue, ^{ 17 NSLog(@"current3%@, main3%@", [NSThread currentThread], [NSThread mainThread]); 18 }); 19 dispatch_async(queue, ^{ 20 NSLog(@"current4%@, main4%@", [NSThread currentThread], [NSThread mainThread]); 21 }); 22 dispatch_async(queue, ^{ 23 NSLog(@"current5%@, main5%@", [NSThread currentThread], [NSThread mainThread]); 24 }); 25 }
开辟了一个子线程,任务顺序执行
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 第二种创建并行队列的方式 4 // 参数1:系统保留字段,队列名 5 // 参数2:系统提供的宏 6 dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT); 7 8 #pragma mark - 向队列中添加任务 9 dispatch_async(queue, ^{ 10 NSLog(@"current1%@, main1%@", [NSThread currentThread], [NSThread mainThread]); 11 }); 12 dispatch_async(queue, ^{ 13 NSLog(@"current2%@, main2%@", [NSThread currentThread], [NSThread mainThread]); 14 }); 15 dispatch_async(queue, ^{ 16 NSLog(@"current3%@, main3%@", [NSThread currentThread], [NSThread mainThread]); 17 }); 18 dispatch_async(queue, ^{ 19 NSLog(@"current4%@, main4%@", [NSThread currentThread], [NSThread mainThread]); 20 }); 21 dispatch_async(queue, ^{ 22 NSLog(@"current5%@, main5%@", [NSThread currentThread], [NSThread mainThread]); 23 }); 24 }
开辟了多个子线程,任务无序执行
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 延迟 4 // 参数1:时间 5 // 参数1.1:DISPATCH_TIME_NOW 表示当前 6 // DISPATCH_TIME_FOREVER 表示遥远的未来; 7 // 参数1.2:距离参数1有多长时间 8 // 参数2:队列 9 // 参数3:block 10 11 dispatch_async(dispatch_get_main_queue(), ^{ 12 NSLog(@"视图加载完成"); 13 }); 14 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 15 NSLog(@"3.0秒之后"); 16 }); 17 18 }
延迟成功
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 重复向一个队列中添加多个任务 4 dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT); 5 // 重复向队列中添加任务 6 // block要随便添加一个参数名 7 dispatch_apply(10, queue, ^(size_t index) { 8 NSLog(@"%ld, %@", index, [NSThread currentThread]); 9 }); 10 }
添加成功
要实现多个任务并行执行,所有任务执行完毕之后执行某个操作时,建议用队列组
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 #pragma mark - 分组 4 // 创建一个分组 5 dispatch_group_t group = dispatch_group_create(); 6 // 创建一个并行队列 7 dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT); 8 // 创建任务 9 dispatch_group_async(group, queue, ^{ 10 NSLog(@"任务1"); 11 }); 12 dispatch_group_async(group, queue, ^{ 13 NSLog(@"任务2"); 14 }); 15 dispatch_group_async(group, queue, ^{ 16 NSLog(@"任务3"); 17 }); 18 // 用于监听所有任务的执行情况 19 // 代码必须不能写在第一个任务之前,否则不能监听全部任务, 20 dispatch_group_notify(group, queue, ^{ 21 NSLog(@"完事儿了"); 22 }); 23 24 }
监听成功
1 #import "MyHandle.h" 2 3 4 static MyHandle *myHandle = nil; 5 @implementation MyHandle 6 7 #pragma mark - 传统单例写法 8 // 此时如果多个任务并发执行,就不会只初始化一次 9 //+ (instancetype)sharedMyHandle { 10 // if (myHandle == nil) { 11 // myHandle = [[MyHandle alloc] init]; 12 // } 13 // return myHandle; 14 //} 15 16 #pragma mark - 完整的单例写法 17 + (instancetype)sharedMyHandle { 18 // 保证在GCD多线程情况下只执行一次 19 static dispatch_once_t onceToken; 20 dispatch_once(&onceToken, ^{ 21 myHandle = [[MyHandle alloc] init]; 22 }); 23 return myHandle; 24 } 25 @end
1 // 函数 2 void function(void * str){ 3 NSLog(@"这是一个函数,%s",str); 4 } 5 6 - (void)viewDidLoad { 7 [super viewDidLoad]; 8 // 第一个参数:队列 9 // 第二个参数:函数参数的内容,注意不要写成OC中的字符串 10 // 第三个参数:函数 11 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 12 dispatch_async_f(queue, "passValue", function); 13 14 }
多线程并行编程中,线程间同步与互斥是一个很有技巧的也很容易出错的地方。
线程间互斥应对的是这种场景:
多个线程操作同一个资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确。
典型的例子是“售票厅售票应用”。售票厅剩余20张票,10个窗口去卖这些票。这10个窗口,就是10条线程,售票厅就是他们共同操作的资源,其中剩余的20张票就是这个资源的一个状态。线程买票的过程就是去递减这个剩余数量的过程。
我们看看会发生什么问题
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // 模拟买票系统 4 // 一共20张票,10个窗口卖 5 __block NSInteger count = 20; 6 dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); 7 for (int i = 0; i < 10; i ++) { 8 dispatch_async(ticketQueue, ^{ 9 //这里相当于每个窗口卖2张票 10 for (int i = 0; i < 2; i ++) { 11 NSLog(@"买到了第%ld张票",count); 12 count--; 13 } 14 }); 15 } 16 }
不同“售票窗口”贩卖了同一张“票”
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // 模拟买票系统 4 // 一共20张票,10个窗口卖 5 __block NSInteger count = 20; 6 __weak typeof(self) weakSelf = self; 7 dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); 8 for (int i = 0; i < 10; i ++) { 9 dispatch_async(ticketQueue, ^{ 10 // 给买票操作加锁,保证代码块只有一个线程访问 11 @synchronized(weakSelf) { 12 //这里相当于每个窗口卖2张票 13 for (int i = 0; i < 2; i ++) { 14 NSLog(@"买到了第%ld张票",count); 15 count--; 16 } 17 } 18 19 }); 20 } 21 }
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // 模拟买票系统 4 // 一共20张票,10个窗口卖 5 __block NSInteger count = 20; 6 7 // 创建线程锁 8 NSLock *lock = [[NSLock alloc]init]; 9 dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT); 10 for (int i = 0; i < 10; i ++) { 11 dispatch_async(ticketQueue, ^{ 12 // 给买票操作加锁,保证代码块只有一个线程访问 13 // 加锁,不会出现多个窗口同时卖一张票的情况 14 [lock lock]; 15 //这里相当于每个窗口卖2张票 16 for (int i = 0; i < 2; i ++) { 17 NSLog(@"买到了第%ld张票",count); 18 count--; 19 } 20 // 解锁 21 [lock unlock]; 22 }); 23 } 24 }
标签:
原文地址:http://www.cnblogs.com/fearlessyyp/p/5503392.html