【iOS】从实际出发理解多线程(二)--NSThread高级 *** 作

【iOS】从实际出发理解多线程(二)--NSThread高级 *** 作,第1张

概述上篇文章讲了一下NSThread的基本 *** 作,本篇讲一下NSThread的一些高级用法。 线程间资源共享&线程加锁 在程序运行过程中,如果存在多线程,呢么各个线程读写资源就会存在先后、同时读写资源的 *** 作,因为实在不同线程中,CPU调度过程中我们无法保证哪个线程会先读写资源,哪个线程后读写资源。这就有可能 *** 作数据混乱和错误。因此为了防止数据读写混乱和错误的发生,我们要将线程在读写数据时加锁,这样就能保

上篇文章讲了一下NSThread的基本 *** 作,本篇讲一下NSThread的一些高级用法。

线程间资源共享&线程加锁

在程序运行过程中,如果存在多线程,呢么各个线程读写资源就会存在先后、同时读写资源的 *** 作,因为实在不同线程中,cpu调度过程中我们无法保证哪个线程会先读写资源,哪个线程后读写资源。这就有可能 *** 作数据混乱和错误。因此为了防止数据读写混乱和错误的发生,我们要将线程在读写数据时加锁,这样就能保证 *** 作同一个数据UI小的线程只有一个,当这个线程执行完成之后解锁,其他的线程才能 *** 作此数据对象。NSLock/NSConditiongLock/NSRecursivelovk/@synchronized都可以实现线程上锁的 *** 作

1.@synchronized

直接上例子:12306抢火车票

// 首先:开启两个线程同时售票self.tickets = 20;NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];t1.name = @"售票员A";[t1 start];    NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];t2.name = @"售票员B";[t2 start];// 然后:将售票的方法加锁- (voID)saleTickets{    while (YES) {        [NSThread sleepForTimeInterval:1.0];        //互斥锁 -- 保证锁内的代码在同一时间内只有一个线程在执行        @synchronized (self){            //1.判断是否有票            if (self.tickets > 0) {                //2.如果有就卖一张                self.tickets --;                NSLog(@"还剩%d张票  %@",self.tickets,[NSThread currentThread]);            }else{                //3.没有票了提示                NSLog(@"卖完了 %@",[NSThread currentThread]);                break;            }        }    }}

 2.NSLock

-(BOol)tryLock;//尝试加锁,成功返回YES ;失败返回NO ,但不会阻塞线程的运行-(BOol)lockBeforeDate:(NSDate *)limit;//在指定的时间以前得到锁。YES:在指定时间之前获得了锁;NO:在指定时间之前没有获得锁。该线程将被阻塞,直到获得了锁,或者指定时间过期。- (voID)setname:(Nsstring*)newname//为锁指定一个name- (Nsstring*)name//**返回锁指定的**name@property (nullable,copy) Nsstring *name;线程锁名称 

举个例子

NSLock *myLock = [[NSLock alloc] init];    static Nsstring *str = @"hello";    [NSThread detachNewThreaDWithBlock:^{        NSLog(@"%d",[myLock tryLock]);        [myLock lock];        NSLog(@"%d",[myLock tryLock]);        NSLog(@"%@",str);        str = @"123";        [myLock unlock];    }];        [NSThread detachNewThreaDWithBlock:^{        [myLock lock];        NSLog(@"%@",str);        str = @"11111";        [myLock unlock];    }];

输出结果不加锁之前,两个线程输出一样都是hello;枷锁之后,输出就会改变hello和123.

3.NSConditionLock

使用此锁,在线程没有获得锁的情况下,阻塞,即暂停运行,典型用于生产者/消费者模型。

- (instancetype)initWithCondition:(NSInteger)condition;//初始化条件锁- (voID)lockWhenCondition:(NSInteger)condition;//加锁 (条件是:锁空闲,即没被占用;条件成立)- (BOol)tryLock; //尝试加锁,成功返回TRUE,失败返回FALSE- (BOol)tryLockWhenCondition:(NSInteger)condition;//在指定条件成立的情况下尝试加锁,成功返回TRUE,失败返回FALSE- (voID)unlockWithCondition:(NSInteger)condition;//在指定的条件成立时,解锁- (BOol)lockBeforeDate:(NSDate *)limit;//在指定时间前加锁,成功返回TRUE,失败返回FALSE,- (BOol)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;//条件成立的情况下,在指定时间前加锁,成功返回TRUE,失败返回FALSE,@property (Readonly) NSInteger condition;//条件锁的条件@property (nullable,copy) Nsstring *name;//条件锁的名称

举个例子:

NSConditionLock *conditionLock = [[NSConditionLock alloc] init];    [NSThread detachNewThreaDWithBlock:^{        for (int i = 0; i < 5; i++) {            [conditionLock lock];            NSLog(@"当前解锁条件:%d",i);            sleep(2);            [conditionLock unlockWithCondition:i];            BOol isLocked = [conditionLock tryLockWhenCondition:2];            if (isLocked) {                NSLog(@"%d加锁成功!!!!!",i);                [conditionLock unlock];            } else {                NSLog(@"%d加锁失败!!!!!",i);            }                    }    }];

输出结果:

4.NSRecursiveLock

此锁可再同一线程中多次被使用,但要保证加锁和解锁使用平衡,多用于递归函数,防止死锁。

- (BOol)tryLock;//尝试加锁,成功返回TRUE,失败返回FALSE- (BOol)lockBeforeDate:(NSDate *)limit;//在指定时间前尝试加锁,成功返回TRUE,失败返回FALSE@property (nullable,copy) Nsstring *name;//线程锁名称

举个例子:

-(voID)initRecycle:(int)value{    [self.myRecursive lock];    if(value>0)    {        NSLog(@"当前的value值:%d",value);        sleep(2);        [self initRecycle:value-1];    }    [self.myRecursive unlock];}

线性安全之原子属性atomic

原子属性(线程安全)与非原子属性,是什么意思呢?

苹果系统在我们声明对象属性时默认是atomic,也就是在读写这个属性时,保证同一时间内只有一个线程能都执行,当声明时用的是atomic,通常会生成_成员变量,如果同时重写了getter&setter方法,_a成员变量就不自动生成。实际上原子属性内部有一个锁,叫做自旋锁。

自旋锁和互斥锁

共同点都能够保证线程安全不同点互斥锁:如果其他线程正在执行锁定的代码,此线程就会进入休眠状态,等待锁打开;然后被唤醒自旋锁:如果线程被锁在外面,那么就会用死循环的方式一直等待锁打开!自旋锁是一种互斥锁的实现方式,相比较一般的互斥锁会在等待期间放弃cpu,自旋锁则是不断循环并测试锁的状态,会一直占用cpu。互斥锁:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。临界区:每个进程中访问临界资源的那段程序称为临界区,每次只允许一个进程进入临界区,进入后不允许其他进程进入。自旋锁:与互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。用在以下情况:锁持有的时间短,而且线程并不希望在重新调度上花太多的成本。"原地打转"。信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

无论什么锁,都很消耗性能,效率不高,所以在我们平时开发过程中,会使用nonatomic。

举个例子

@property (strong,nonatomic) NSObject *myNonatomic;@property (strong,atomic) NSObject *myAtomic;// 当我们重写了myAtomic的setter和getter方法时- (voID)setMyAtomic:(NSObject *)myAtomic{      _myAtomic = myAtomic;}- (NSObject *)myAtomic{    return _myAtomic;}// 那么我们就必须声明一个_myAtomic静态变量@synthesize myAtomic = _myAtomic;// 否则系统在编译的时候找不到 _myAtomic

子线程的Runloop

在介绍子线程上的Runloop之前先来一个有意思的小插曲,我们来介绍一下Runloop,甚至模拟一个Runloop

Runloop 运行循环
-在目前iOS开发中,几乎用不到,在以前iOS黑暗时代,程序员会用到
目的:
保证程序不退出
监听事件
没有事件让程序进入休眠
区分模式:
NSDefaultRunLoopMode - 时钟、网络事件
NSRunLoopCommonModes - 用户交互

// 模拟runloopvoID click(int type){    printf("正在运行第%d",type);}int main(int argc,const char * argv[]) {    @autoreleasepool {        while (YES) {            printf("请输入选项 0 表示退出");            int result = -1;            scanf("%d",&result);            if (result == 0) {                printf("程序结束\n");                break;            }else{                click(result);            }        }    }    return 0;}
在iOS中,开辟的子线程上的Runloop是默认不开启的,并且子线程中的Runloop开启之后是手动无法关闭的。那么当我们给子线程中重复添加不同任务时并且Runloop没有开启的情况下,子线程无法监听事件(确切说是子线程的Runloop),我们后来添加的任务就无法执行。
但是我们如果让子线程Runloop一直工作又浪费资源,下面介绍一个OC中常用到的可以控制子线程Runloop的例子:
首先,Runloop就是一个死循环,那么我们就创建一个死循环,然后声明一个可以判断是否应该退出Runloop循环的属性
@property (assign,nonatomic,getter=isFinished) BOol finished;// 创建子线程并添加任务NSThread *t = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];[t start];self.finished = NO;[self performSelector:@selector(otherMethod) onThread:t withObject:nil waitUntilDone:NO];// 在第一个任务中加入死循环- (voID)demo{    NSLog(@"%@",[NSThread currentThread]);    //在OC中使用比较多的,退出循环的方式    while (!self.isFinished) {        [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:.1]];    }    NSLog(@"能来吗?");}// 在最后添加的任务结束后结束死循环- (voID)otherMethod{    for (int i = 0; i < 10; i ++) {        NSLog(@"%s   %@",__FUNCTION__,[NSThread currentThread]);    }  //让上面方法中的死循环结束   self.finished = YES;}
总结

以上是内存溢出为你收集整理的【iOS】从实际出发理解多线程(二)--NSThread高级 *** 作全部内容,希望文章能够帮你解决【iOS】从实际出发理解多线程(二)--NSThread高级 *** 作所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1055918.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存