标签:
在这里给大家介绍一些多线程的知识,以及应用,希望能给一些需要的朋友学习学习,如果有错误的地方,请帮忙指出,非常感谢。
那么先介绍多线程前,先说一下什么是线程,什么是进程?
进程:{ 1.正在运行的一个应用程序就叫进程。
2.每个进程之间都是相互独立的,每个进程都运行在自己独立的专用的且受保护的内存空间内。
}
线程:{ 1.线程是进程的基本执行单元。
2.每一个进程都默认开启一条线程,我们称之为主线程。(一个进程至少有一条线程)
}
多线程:多线程就是一个进程可以开辟多线线程(子线程),同时执行不同的任务。(多线程可以解决线程阻塞的问题, 并且提高了程序的运行效率)
多线程的执行原理:{ 1.(单核CUP),同一时间,CUP只能处理一条线程, 只有一条线程在执行。
2.多线程的同时执行:是CUP在多个线程中快速切换。
3.CUP调度线程的时间够快,就产生了多线程的"同时"执行。
}
多线程的优缺点:优点:{ 1. 提高了程序的执行效率。
2. 提高了CUP和内存的资源利用率。
3.当子线程执行完任务后,会自动销毁。
}
缺点:{ 1.每条线程都占用512KB的内存空间。
2.开辟大量的线程,增加了CUP在调度上的开销。
3.开辟大量的线程,消耗了大量内存,降低的程序的性能。
4.增加的程序的设计难度,比如多线程访问共享资源数据安全问题,以及线程间的通讯。
}
主线程:{主线程以一个程序运行后,默认开辟的一条线程,我们称之为主线程,也叫UI线程。
主线程一般用来刷新UI界面,处理UI事件。
}
注意:不要将耗时操作放在主线程上执行,耗时操作会卡住主线程,造成UI界面卡顿,影响界面流畅度。
什么是耗时操作? I/O操作就是耗时操作。
iOS中多线程的设计方案:
1. pthread的使用
导入头文件 : #import <pthread.h>
pthread_t pthread;
第一个参数:&pthread 线程编号地址
第二个参数:线程的属性
第三个参数:要执行的方法
第四个参数:要执行的方法的参数
如果创建子线程成功,将会返回0,其他则失败。
int result = pthread_create(&pthread, NULL, demo, NULL);
2.NSThread的使用
方式1:
[NSThread * thread = [NSThread alloc]initWithTarget:self selector: @selector(demo) object:nil] ;
[thread start];
方式2:
[NSThread detachNewThreadSelector:@selector: (demo) toTarget: self withObject :nil];
方式3:
[self performSelectorInBackground:@selector:(demo) withObject:nil];
线程的状态:新建,就绪,运行,阻塞,死亡(正常死亡,非正常死亡)。
控制线程的状态:
1.启动线程:
-(void)start; 让线程进入就绪状态,线程执行完毕后(若一直处于空闲)进入死亡状态。
2.暂停(阻塞)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti; 让线程进入阻塞状态,时间到时进入就绪状态。
3.停止线程
+ (void)exit;
线程进入死亡状态 , 注意一旦线程死亡,就不会再开启任务。
线程的属性:{ 线程的名称:设置线程的名称可以当线程执行方法内部出现异常的时候记录异常和当前线程。(根据线程的name属性,设置)
线程的优先级:内核调度算法在决定该运行哪个线程时,会把线程的优先级作为考量因素,较高优先级的线程会比较低优先级的线程具有更多的运行机会。较高优先级不保证你的线 程具体执行的时间,只是相比较低优先级的线程,它更有可能被调度器选择执行而已。(thread setThreadPriority:<#(double)#>设置优先级)
多线程访问共享资源的问题:
共享资源:一块资源被多个线程共享,也就是多个线程可以访问这个资源。(一个文件, 一个变量,一个对象)
当多个线程访问同一块资源时,很容易造成数据错乱和数据安全问题。
比如: 同时访问同一个变量,导致如下问题。
为了解决多线程访问共享资源数据安全为题,于是引入了互斥锁。
互斥锁使用:
@synchronized(锁对象) { // 需要锁定的代码 }
互斥锁
相关专业术语:线程同步
互斥锁原理
互斥锁执行的五个步骤:
线程执行到synchronized
i. 检查锁状态 如果是开锁状态(1)转到ii 如果上锁(0)转到v
ii. 上锁(0)
iii.执行代码块
iv. 执行完毕 开锁(1)
v.线程等待(就绪状态)
加锁后程序执行的效率比不加锁的时候要低,因为要线程要等待锁,但是锁保证了多个线程同时操作全局变量的安全性
属性中的修饰符
保证同一时间只有一个线程能够写入(但是同一个时间多个线程都可以取值)
atomic 本身就有一把锁(自旋锁)
单写多读:单个线程写入,多个线程可以读取
GCD:
执行任务的方式(同步, 异步)
第一个参数:队列
都二个参数:任务,操作
dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
队列的类型(串行队列 , 并发队列 )
并发队列: 可以让多个任务并发执行, (自动开启了多个线程)
并发功能只在异步函数中有效
串行队列:让任务一个接一个完成(一个任务完毕后,再执行下一个任务)
总结:
同步异步决定是否开启新的线程:{ 1, 同步:在当前线程中执行任务,不具备开辟新的线程的能力
2. 异步:在新的线程中执行任务,具备有开辟新线程的能力 }
并发和串行决定执行任务的方式 : { 1, 并发:多个任务同时执行
2.一个任务执行完毕后,再执行下个任务}
串行队列,同步执行
不开线程,同步执行(在当前线程执行)
#define DISPATCH_QUEUE_SERIAL NULL
//串行队列
//dispatch_queue_t q = dispatch_queue_create("test", NULL);
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i++) {
//同步执行
dispatch_sync(q, ^{
NSLog(@"%@ -- %d",[NSThread currentThread],i);
});
}
串行队列,异步执行
开一个线程,顺序执行
//只有一个线程,因为是串行队列,只有一个线程就可以按顺序执行队列中的所有任务
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 10; i++) {
//异步执行
dispatch_async(q, ^{
NSLog(@"%@ -- %d",[NSThread currentThread],i);
});
}
并行队列,异步执行
开多个线程,异步执行
开多个线程,异步执行,每次开启多少个线程是不固定的(线程数,不由我们控制),线程数是由gcd来决定的
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
//异步执行
dispatch_async(q, ^{
NSLog(@"%@ -- %d",[NSThread currentThread],i);
});
}
并行队列,同步执行
不开线程,顺序执行
主队列:
主队列,异步执行
主队列,同步执行
//创建队列
dispatch_queue_t mainQueue = dispatch_get_main_queue(); //2.任务 dispatch_block_t task1 = ^ { [NSThread sleepForTimeInterval:1.0]; NSLog(@"task1 %@",[NSThread currentThread]); }; dispatch_block_t task2 = ^ { NSLog(@"task2 %@",[NSThread currentThread]); }; //同步 dispatch_sync(mainQueue, task1); dispatch_sync(mainQueue, task2);
如何解决死锁?(将主队列操作放入子线程中执行)
//异步 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //主队列 主队列执行任务只有当主线程空闲的时候才能够执行 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //2.任务 dispatch_block_t task1 = ^ { [NSThread sleepForTimeInterval:1.0]; NSLog(@"task1 %@",[NSThread currentThread]); }; dispatch_block_t task2 = ^ { NSLog(@"task2 %@",[NSThread currentThread]); }; //同步 dispatch_sync(mainQueue, task1); dispatch_sync(mainQueue, task2); });
全局队列: 全局队列本质就是并发队列.(通常使用全局队列, 容易获取)
全局队列和并发队列的区别: {1.全部队列就只有一个, 并发队列可以有多个. 在MRC下,并发队列creat,就需要release , 而全局队列不需要release.
2. 并发队列有名称, 可以跟踪错误,全局队列没有
}
各种队列的执行效果:
GCD的其他用法:
延迟操作(异步)
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#>
});
一次性执行: (业内称一次性执行是为单例而生, 并且一次性执行是线程安全的)
//如果onceToken为0的话就可以执行代码块,如果为-1则直接跳过代码块
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
dispatch_once(&onceToken, ^{
});
NSLog(@"end%ld",onceToken);
调度组:应用场景:当多个任务同时无序执行,并且当这些任务完成后,提示用户执行完成.
//调度组 dispatch_group_t group = dispatch_group_create(); /* 参数1 :调度组 参数2:队列 参数3:任务 */ // 第一首歌曲下载 dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"第一首歌曲下载"); }); // 第二首歌曲下载 dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"第二首歌曲下载"); }); // 第三首歌曲下载 dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"第三首歌曲下载"); }); //如果组里面的任务执行完毕,通知执行下面的代码 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"%@",[NSThread currentThread]); NSLog(@"歌曲下载完成"); });
NSOperation:
使用步骤:与GCD类似
NSInvocationOperation:
创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- (void)start;
一旦执行操作,就会调用target的sel方法
注意:
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。
NSBlockOperation:
+ (id)blockOperationWithBlock:(void (^)(void))block;
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; //操作添加额外的任务 [op addExecutionBlock:^{ NSLog(@"Execution %@",[NSThread currentThread]); }]; [op start]; //如果NSBlockOperation的操作数>1 开启新的线程 2016-03-06 18:53:58.719 15-NSBlockOperation[9384:1039444] <NSThread: 0x7fdb60e02e90>{number = 1, name = main} 2016-03-06 18:53:58.720 15-NSBlockOperation[9384:1039488] Execution <NSThread: 0x7fdb60c0f620>{number = 2, name = (null)}
NSOperationQueue:
NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
线程间通信:
主队列: 添加到主队列的操作,最终都执行在主线程上.
[NSOperationQueue mainQueue];
当前队列: 获取当前队列 [SNOperationQueue currentQueue];
NSOperation 和GCD 的差异:
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作 (取消还没执行的操作, 正在执行的操作继续执行,直到完成 )
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列 (正在执行的操作完毕后, 暂停)
- (BOOL)isSuspended;
操作的优先级: 操作的优先级并不能控制操作的执行时间,只是相对与优先级低的操作, 线程调用的频率高罢了.
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
监听操作:
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
操作依赖:
比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
可以在不同queue的NSOperation之间创建依赖关系
注意:操作之间,不能相互依赖.
这些就是多线程的基础知识, 若有问题或不全, 请大神指出, 感谢.
网络多线程(pthread , NSThread,GCD ,NSOperation)
标签:
原文地址:http://www.cnblogs.com/yuwei0911/p/5243911.html