多线程(四种实现方式)
1.NSThread
2.NSObject
3.NSOperation/NSOperationQueue
4.GCD
进程:一个独立运行的程序可以看做是一个进程。
线程:进程中的独立代码段。
通常一个程序只有一个进程,一个进程有1-n个线程(最少有一个主线程运行程序)
只有主线程的程序叫单线程程序,有主线程和子线程的程序称为到线程程序
<h6>应用程序会默认为主线程分配1M的栈空间,默认为子线程分为512K的栈空间(分配必须是4K的整数倍)。主线程和子线程的栈区是相互独立的。</h6>
<h6>主线程从main函数开始,所有的内容全部在自动释放池管辖范围内。子线程新开辟的区域在处理任务时,并没有在自动释放池的管辖范围内,若在堆区开辟空间而不进行内存处理,会造成大量的泄漏(比如短时间内循环使用便利构造器等),所以人为开辟子线程必须要添加@autoreleasepool来管辖堆区的内容。主线程和子线程的堆区是共有的。
<1>NSThread
1.1使用示例对象来开辟子线程(手动开启)
// 参数:方法的执行者、子线程需要执行的方法、传递的参数
NSThread *thread =[[NSThread alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
[thread start];// 必须手动开动才会执行子线程内容
// 取消子线程
// [thread cancel];
// 查看当前线程是否正在执行
NSLog(@"在执行%d",[thread isExecuting]);
// 查看当前线程是否取消
NSLog(@"已取消%d",[thread isCancelled]);
// 打印当前方法是否为主线程
NSLog(@"是否为主线程%d",[NSThread isMainThread]);
// 打印当前线程
NSLog(@"当前线程%@",[NSThread currentThread]);
1.2使用静态方法来开辟子线程(自动开启)
[NSThread detachNewThreadSelector:@selector(calculateAction) toTarget:self withObject:nil];
<2>NSObject
[self performSelectorInBackground:@selector(calculateAction) withObject:nil];
NSLog(@"主线程中开辟子线程代码.....");
<3>NSOperationQueue:
此种多线程方式和NSOperation的两个子类来结合使用,实现多线程方式
// 3.1
NSInvocationOperation *invocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(calculateAction) object:nil];
// 3.2
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
[self calculateAction];
}];
// 创建管理队列的两个任务
NSOperationQueue *queue = [NSOperationQueue new];
// 设置最大并发数(同时执行任务的个数)
// 若最大并发数设置为1,那么任务将按照串行方式来执行
[queue setMaxConcurrentOperationCount:1];
// 将任务添加到队列
[queue addOperation:invocation];
[queue addOperation:block];
<4>GCD
// 两种队列方式:并行队列,串行队列
// 并行队列:所有任务并发执行,不分先后
// 串行队列:所有任务依次执行,排队完成
// GCD中,有三种队列可以使用
// 主队列:系统提供的单列,属于串队列
// 全局队列:系统提供的单列,属于并行队列
// 自定义队列:开发人员可以自定义选择使用串行或者并行
4.1主队列(int a = 10;主队列的任务在主线程中执行)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 给队列添加任务
dispatch_async(mainQueue, ^{
NSLog(@"第一个任务%d",[NSThread isMainThread]);
});
dispatch_async(mainQueue, ^{
NSLog(@"第二个任务%d",[NSThread isMainThread]);
});
dispatch_async(mainQueue, ^{
NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
4.2全局队列:并行队列,会开辟子线程,但是其管理的任务不一定只在子线程执行,也会添加到主线程执行
// 1.队列优先级
// 2.空余参数,以后添加作用
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 必须先执行我(第一个加入队列时,才会优先执行此任务(在主线程中执行),否则和其他三个任务一样无法确认哪个先执行)
dispatch_barrier_sync(globalQueue, ^{// 此处是_sync不是_async
NSLog(@"优先执行的代码......%d",[NSThread isMainThread]);
});
// 向队列中添加任务(三个任务在子线程中同步执行,打印结果没有固定的先后顺序)
dispatch_async(globalQueue, ^{
NSLog(@"第一个任务%d",[NSThread isMainThread]);
});
dispatch_async(globalQueue, ^{
NSLog(@"第二个任务%d",[NSThread isMainThread]);
});
dispatch_async(globalQueue, ^{
NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
延时启动任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), globalQueue, ^{
NSLog(@"出来吧,神龙!");
});
反复执行n次的任务
dispatch_apply(3, globalQueue, ^(size_t time) {
NSLog(@"第%zu次",time);
});
函数
void function(void * string);
void function(void * string){
NSLog( @"%s",string);
}
添加此函数到队列
// 1.要把任务添加到的队列
// 2.函数的参数
// 3.函数
dispatch_async_f(globalQueue, "fall in love with", function);
4.3自定义队列
串行队列
// 1.给队列一个标签,后续可以获取队列的标签来操作
dispatch_queue_t serialQueue = dispatch_queue_create("com.lanou3g.mySerialQueue", DISPATCH_QUEUE_SERIAL);
// async和sync添加任务的区别在于是否同时执行block和外部的代码(即子线程和主线程是否同时执行),前者会同时执行,后者则是顺序执行
dispatch_async(serialQueue, ^{
NSLog(@"第一个任务%d",[NSThread isMainThread]);
});
NSLog(@"1");
dispatch_async(serialQueue, ^{
NSLog(@"第二个任务%d",[NSThread isMainThread]);
});
NSLog(@"2");
dispatch_async(serialQueue, ^{
NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
NSLog(@"3");
并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
/*
dispatch_async(concurrentQueue, ^{
NSLog(@"第一个任务%d",[NSThread isMainThread]);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"第二个任务%d",[NSThread isMainThread]);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"第三个任务%d",[NSThread isMainThread]);
});
*/
/*
// 分组:使用一个组来操作一组任务
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"第一个分组任务%d",[NSThread isMainThread]);
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"第二个分组任务%d",[NSThread isMainThread]);
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"第三个分组任务%d",[NSThread isMainThread]);
});
// 等待分组中所有任务执行完毕后再执行此任务(必须写在后面)
dispatch_group_notify(group, concurrentQueue, ^{
NSLog(@"撩汉终结者,kingStar");
});
*/
子线程回到主线程的演示
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 添加子任务
dispatch_async(globalQueue, ^{
[self calculateAction];
});
// 主、全局无论哪种队列都遵循FIFO(先进先出)
用来耗时的计算(多线程测试的使用)
- (void)calculateAction{
// 让子线程睡10秒再干活
[NSThread sleepForTimeInterval:2];
// 当子线程中出现对象类型时,需要使用自动释放池包裹对应的代码
@autoreleasepool {
int sum = 0;
for (int i = 0; i < 655350000; i++ ){
sum += i;
}
NSLog(@"%d",sum);
// 子线程回到主线程的第一种方式
// 1.执行在主线程的方法
// 2.传递参数
// 3.是否等到完成后操作
NSNumber *nsnum = [NSNumber numberWithInt:sum ];
[self performSelectorOnMainThread:@selector(mainAction:) withObject:nsnum waitUntilDone:YES];
// 打印当前方法是否为主线程
NSLog(@"===%d",[NSThread isMainThread]);
// 打印当前线程
NSLog(@"===%@",[NSThread currentThread]);
// 子线程回到主线程的第二种方式
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"%d",sum);
NSLog(@"%d",[NSThread isMainThread]);
});
}
}
// 子线程回到主线程的第一种方式调用的mainAction:方法
- (void)mainAction:(NSNumber *)sum{
NSLog(@"计算的结果为%d",[sum intValue]);
NSLog(@"当前线程是否为主线程%d",[NSThread isMainThread]);
}
线程按照生命周期分为两种
脱离线程:线程使用完毕后,即被销毁,不可在唤醒
非脱离线程:线程任务执行时,始终处于被唤醒的状态,一旦收集到任务,马上启动,生命周期长,可被唤醒。
// 多线程操作(两队人购票)
// 初始化开辟空间
self.lock = [NSLock new];
// 多线程容易出现线程互斥问题
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
// 第一队人在购票
for (int i = 0; i < 1000; i++) {
[self buyTicket:@"王冲"];
}
});
dispatch_async(globalQueue, ^{
// 第二队人在购票
for (int i = 0; i < 1000; i++) {
[self buyTicket:@"JonKing"];
}
});
// atomic和nonatomic实现
// atomic是线程安全的,使用@synchronized进行对对象的加锁操作,但是只能是对象类型。
// nonatomic是线程不安全的,没有对数据做任何处理。
// 举例代码:下列代码的意思:当访问到self对象时,写在大括号内部的代码会被保证同一时间只允许一个任务在访问。
@synchronized(self) {
// 操作
}
购票方法
int count = 5000;
- (void)buyTicket:(NSString *)str{
// 每次遭遇访问时,要进行加锁操作
[self.lock lock];
count -- ;// 模拟每次买票造成的结果,票数减一
NSLog(@"%@购的一张票,剩余%d张票",str,count);
// 资源访问完毕时,进行解锁让下一个任务访问
[self.lock unlock];
}