昨天忙碌了一天没有继续做自己的笔记,今天继续。今天主要是加深一下自己对GCD、NSOperation、NSthread的理解和使用吧。
1.进程和线程的区别?同步异步的区别?并行和并发的区别?
进程、线程的叫法是相对的。每一个应用运行的进程相对于一台电脑、一个手机来说是线程。但对于一个应用来说,我们会在应用的进程里边开辟很多线程,来处理不同的耗时的任务。
进程:有独立的地址空间,一个进程崩溃之后,在保护模式下不会对其他进程产生影响。进程是资源分配的基本单位。
线程:进程中的任务的不同执行路径。线程有自己的堆栈和局部变量,但是没有单独的地址空间。一个线程死掉等同于整个进程死掉。所以多进程的程序比多线程的程序健壮。但是进程之间的切换,资源消耗较大。效率低。
联系:二者都是由操作系统所体会的程序运行的基本单元。系统利用该基本单元实现系统对应用的并发性。进程是线程的容器,代码执行在线程中,进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程。一个进程中的所有线程共享当前进程所拥有的资源。
同步:在程序内的调用者发出一个“调用”之后,在没有得到调用结果(CPU返回执行的数据段部分)之前,调用者会一直处于等待的状态,直至结果返回,才会执行下一个语句。换句话说,就是由调用者主动等待这个“调用”的结果。并不会耽误CPU处理其他的事件。
异步:调用者发出“调用”之后,调用会直接返回。没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在“调用”发出后,“被调用者”通过状态、通知来通知调用者,或通过回调函数处理这个调用。
并行:CPU的多核芯同时执行多个任务。
并发:单核CPU交替执行两个任务。
2,线程间通信?
线程间通讯的体现:
(1)一个线程传递数据给另一个线程,如:网络数据的异步线程请求,回归主线程进行赋值,刷新UI。
(2)在一个线程中执行完特定任务之后,转到另一个线程继续执行任务。
线程间通信常用的方法:
(1)NSThread`可以先将自己的当前线程对象注册到某个全局的对象中去,这样相互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在主线程执行的方法。
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5,2_0);
(2)GCD,一个线程的数据传递给另一个线程。
- (void)touchesBegan:(NSSet*)touches withEvent(UIEvent*)event
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
NSLog(@"donwload---%@", [NSThreadcurrentThread]);// 1.子线程下载图片
NSURL*url = [NSURLURLWithString:@"http://d.jpg"];
NSData*data = [NSDatadataWithContentsOfURL:url];
UIImage*image = [UIImageimageWithData:data];
// 2.回到主线程设置图片
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"setting---%@ %@", [NSThreadcurrentThread], image);
[self.button setImage:image forState:UIControlStateNormal];
});
});
}
3.GCD的一些常用的函数?(group,barrier,信号量,线程同步)
延迟执行任务函数:
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#>
});
只执行一次的函数
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
<#code to be executed once#>
});
栅栏函数
dispatch_barrier_sync 同步函数,会阻塞线程
dispatch_barrier_async 异步函数,不会阻塞线程
如果所有任务都在同一个并行队列中,并且这个并行队列不是系统自带的全局并行队列,那么在barrier之前添加的任务执行完毕之后才会执行barrier任务。只有barrier任务执行完毕之后才会执行barrier之后添加的任务。
dispatch_barrier_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
dispatch_barrier_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
队列的使用
dispatch_group_async(<#dispatch_group_t _Nonnull group#>, <#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
定时器
/*
第一个参数:给那个定时器设置
第二个参数:什么时候启动
第三个参数:间隔多久执行一次
第四个参数:设置精准度:0 代表最高精准(尽量让定时器精准), 大于0的的话代表是在多少秒内接受.
第四个参数存在意义:主要是为了提高程序性能, 设置越大,能减轻CPU的压力
注意:GCD定时器传入的时间都是纳秒
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, <#dispatchQueue#>);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, <#intervalInSeconds#> * NSEC_PER_SEC, <#leewayInSeconds#> * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
<#code to be executed when timer fires#>
});
dispatch_resume(timer);
信号量
假设现在系统由两个空闲资源可以被利用,但是同一时间却有三个线程进行访问。这个时候就可以方便的利用信号量来解决。
信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问资源之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。类似锁机制。
函数:
//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
//提高信号量
dispatch_semaphore_signal(信号量)
注:正常的使用顺序是先降低然后再提高。两个函数通畅是成对使用。
代码示例:
GCD信号量的使用4.如何使用队列来避免资源抢夺?
将需要访问同一块资源的任务添加到一个串行队列执行。队列内部可根据需求来决定是否需要异步或者同步。
如果不使用队列,可以添加线程锁。有以下方法:
(1)@synchronized(id anObject)-->最简单的方法
会自动对参数对象进行加锁,保证临界区内的代码线程安全。
截图1@synchronized (<#token#>) {
// 这段代码对其他 @synchronized(self) 都是互斥的
// self 指向同一个对象
<#statements#>
}
(2)采用NSLock
NSLock对象实现了NSLocking protocol,包含几个方法:
lock 加锁
unlock 解锁
tryLock 尝试请求一个锁,如果失败了,并不会阻塞线程,只是立即返回NO。
lockBeforeDate,在指定的date之前暂时性阻塞线程(如果没有获取锁的话)。如果到期还没有获取锁,线程被唤醒,函数立即返回NO。
(3)NSRecursiveLock,递归锁
NSRecursiveLock,多次调用不会阻塞已获取该锁的线程。
如图这段代码是一个死锁状况。
截图2-死锁 截图3-死锁替换为NSRecursiveLock试一下,运行结果如图:
截图4-NSRecursiveLock(4)NSConditionLock,条件锁
NSConditionLock,条件锁,可以设置条件
[lock lockWhenCondition:A条件];
/*表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它不会得到改锁,等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,执行代码。同时设置它获得该锁,其他线程都将等待它的代码区执行完毕,直至解锁。*/
[lock unlockWithCondition:A条件];
/*表示解锁,同时把内部的condition设置为A条件*/
(5)NSDistributedLock,分布锁
NSDistributedLock,分布锁,文件方式实现。可以跨进程。用tryLock方法获取锁。用unLock方法释放锁。如果一个获取锁的进程,在释放锁之前挂了。那么锁就一直得不到释放,此时可以通过breakLock强行解锁。
(6)使用信号量,限制访问同一块资源的线程数目
网友评论