进程与线程的关系
进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程,称为主线程)。一个进程(程序)的所有任务都在线程中执行,
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
NSThread
NSThread是OC线程的基础类,可以通过start sleep cancel控制线程执行情况。
// 方法一:创建线程,需要自己开启线程(可控制)
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
// 开启线程
[thread start];
// 方法二:创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
// 方法三:隐式创建并启动线程
[self performSelectorInBackground:@selector(run) withObject:nil];
GCD
GCD会自动利用更多的CPU内核
GCD自动管理线程的生命周期(创建线程,调度任务,销毁线程等)
GCD中有2个核心概念:任务和队列
任务
同步:只能在当前线程中执行任务,不具备开启新线程的能力,任务立刻马上执行,会阻塞当前线程并等待 Block中的任务执行完毕,然后当前线程才会继续往下运行
异步:可以在新的线程中执行任务,具备开启新线程的能力,但不一定会开新线程,当前线程会直接往下执行,不会阻塞当前线程
队列
判定一些操作往一个队列中添加时如何执行,先看是往串行还是并行队列中加任务,如是串行必然是顺序执行的,如果是并行异步才会交叉执行。
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
// 并发队列
dispatch_queue_t queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
栅栏函数可以控制任务执行的顺序,栅栏函数之前的执行完毕之后,执行栅栏函数,然后在执行栅栏函数之后的(并发队列)
dispatch_barrier_async(queue, ^{ });
队列组
当有多个异步任务的时候,组内全部执行完毕再执行dispatch_group_notify可以起到监听作用,控制业务流程。
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并行队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 执行队列组任务
dispatch_group_async(group, queue, ^{
NSLog(@"异步任务1");
});
// 执行队列组任务
dispatch_group_async(group, queue, ^{
NSLog(@"异步任务2");
});
//队列组中的任务执行完毕之后,执行该函数
dispatch_group_notify(group, queue, ^{
NSLog(@"组内执行完毕任务,此处执行");
});
信号量
// 创建信号量,并且设置值为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
//发送一个信号,自然会让信号总量加1,
dispatch_semaphore_signal(semaphore);
});
//阻塞主线程
//当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"等待信号量大于0才会执行”);
//利用信号量进行并发控制(5次)
dispatch_semaphore_t sema = dispatch_semaphore_create(5);
for (100次循环操作) {
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 操作
dispatch_semaphore_signal(sema);
});
}
dispatch_group_enter和dispatch_group_leave
利用dispatch_group_enter和dispatch_group_leave控制线程,因为二者必须一一对应,只有leave之后,才能进行下一个enter。如:利用这两个函数实现AFNetWorking多个请求依赖的线程同步问题,这样就能让下一个异步请求在上一个执行完毕后再执行。而不需要写在回调内。
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
AFNetWorking{
dispatch_group_leave(group);
NSLog(@"异步任务1");
}
dispatch_group_enter(group);
AFNetWorking{
dispatch_group_leave(group);
NSLog(@“异步任务2");
}
dispatch_group_notify(group, queue, ^{
NSLog(@"组内执行完毕任务,此处执行");
});
NSOperationQueue
NSOperation 是苹果公司对 GCD 的封装,完全面向对象,并比GCD多了一些更简单实用的功能,所以使用起来更加方便易于理解,主要优势有设置依赖,优先级设置,可继承,键值对观察。NSOperation 和NSOperationQueue 分别对应 GCD 的 任务 和 队列。
NSOperation和NSOperationQueue实现多线程的具体步骤
1.将需要执行的操作封装到一个NSOperation对象中
2.将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来,并将取出的NSOperation封装的操作放到一条新线程中执行
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
3.最大并发数:当maxConcurrentOperationCount为1时,则表示不开线程,也就是串行,默认为-1,大于1都为并行。
4.NSOperationQueue可以cancel,suspend(暂停)。暂停和取消不是立刻取消当前操作,而是等当前的操作执行完之后不再进行新的操作
操作依赖
// 创建非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 下载第一张图片
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
}];
// 下载第二张图片
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
}];
// 合成操作
NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
// 回到主线程刷新UI
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
//合成图片
}];
}];
// 添加依赖,合成图片需要等图片1,图片2都下载完毕之后合成
[combie addDependency:download1];
[combie addDependency:download2];
// 添加操作到队列
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combie];
读写锁
self.concurrentQueue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
// 写操作,栅栏函数是不允许并发的,所以"写操作"是单线程进入的,根据log可以看出来
- (void)setText:(NSString *)text {
__weak typeof(self) weakSelf = self;
dispatch_barrier_sync(self.concurrentQueue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf->_text = text;
NSLog(@"写操作 %@ %@",text,[NSThread currentThread]);
// 模拟耗时操作,1个1个执行,没有并发
sleep(1);
});
}
// 读操作,这个是可以并发的,log在很快时间打印完
- (NSString *)text {
__block NSString * t = nil ;
__weak typeof(self) weakSelf = self;
dispatch_sync(self.concurrentQueue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
t = strongSelf->_text;
// 模拟耗时操作,瞬间执行完,说明是多个线程并发的进入的
sleep(1);
});
return t;
}
互斥锁,模拟抢票,多个线程同时访问和改变一个变量
-(void)saleTicket
{
while (1) {
// 创建对象
// self.obj = [[NSObject alloc]init];
// 锁对象,本身就是一个对象,所以self就可以了
// 锁定的时候,其他线程没有办法访问这段代码
@synchronized (self) {
// 模拟售票时间,我们让线程休息0.05s
[NSThread sleepForTimeInterval:0.05];
if (self.numTicket > 0) {
self.numTicket -= 1;
NSLog(@"%@卖出了一张票,还剩下%zd张票",[NSThread currentThread].name,self.numTicket);
}else{
NSLog(@"票已经卖完了");
break;
}
}
//使用NSLock对象也可以实现
NSLock *lock = [[NSLock alloc]init];
[lock lock];
//Safe
[lock unlock];
}
}
如果将numTicket对象设置为atomic则默认会使用互斥锁,等同于@synchronized (self)
@property(automic,assign)NSInteger numTicket;
一个进程的内存分配
每个区域实际上都存储相应的内容,其中代码区、常量区、静态区这三个区域都是自动加载,并且在进程结束之后被系统释放,开发者并不需要进行关注。
栈区一般存放局部变量、临时变量,由编译器自动分配和释放,每个线程运行时都对应一个栈。而堆区用于动态内存的申请,由程序员分配和释放。一般来说,栈区由于被系统自动管理,速度更快,但是使用起来并不如堆区灵活。
对于 Swift 来说,值类型存于栈区,引用类型存于堆区。值类型典型的有 struct、enum 以及 tuple 都是值类型。而比如 Int、Double、Array,Dictionary 等其实都是用结构体实现的,也是值类型。而 class、closure 都是引用类型,也就是说 Swift 中我们如果遇到类和闭包,就要留个心眼,考虑一下他们的引用情况。
原文地址:https://blog.csdn.net/u010008647/article/details/122358585
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_51347.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!