练习代码地址:
最底层API pthread
: 是一套通用的跨平台的多线程API,是基于c语言,线程的生命周期需要手动管理;
NSThread:是对pthread用OC语言的封装,使得操作线程可以像操作对象一样操作;
使用最多的GCD
:替代NSThread的多线程API,可以充分利用多核技术;执行的子线程任务都在块里(block);其生命周期自行维护;
NSOperation:是基于GCD的面向对象的封装,比起gcd使用更加简单,同时也增加了一些实用功能;
下面代码会发生什么?
NSLog(@"步骤1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"步骤2");
});
NSLog(@"步骤3");
答案是:输出步骤1 后程序会崩溃;看一下实际运行后的截图:
dispatch_get_main_queue()获取的是主队列,主队列等同于串行队列;先来看下iOS中多线程的四个名词:
同步sync,异步async,并行队列Concurrent Dispatch Queue,串行队列Serial Dispatch Queue;
并发队列 | 串行队列 | 主队列 | |
---|---|---|---|
sync | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 |
async | 有开启新线程,并发执行任务 | 有开启新线程,串行执行任务 | 没有开启新线程,串行执行任务 |
通过代码对上表进行验证下:
dispatch_queue_t serialQue = dispatch_queue_create("com.lym.serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQue = dispatch_queue_create("com.lym.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"步骤0");
dispatch_async(serialQue, ^{
NSLog(@"串行队列 异步任务 1");
});
dispatch_async(serialQue, ^{
NSLog(@"串行队列 异步任务 2");
});
dispatch_async(serialQue, ^{
NSLog(@"串行队列 异步任务 3");
});
NSLog(@"------------------------");
dispatch_async(concurrentQue, ^{
NSLog(@"并发队列 异步任务 1");
});
dispatch_async(concurrentQue, ^{
NSLog(@"并发队列 异步任务 2");
});
dispatch_async(concurrentQue, ^{
NSLog(@"并发队列 异步任务 3");
});
NSLog(@"------------------------");
dispatch_sync(concurrentQue, ^{
NSLog(@"并发队列 同步任务 1");
});
dispatch_sync(concurrentQue, ^{
NSLog(@"并发队列 同步任务 2");
});
dispatch_sync(concurrentQue, ^{
NSLog(@"并发队列 同步任务 3");
});
NSLog(@"------------------------");
dispatch_sync(serialQue, ^{
NSLog(@"串行队列 同步任务 1");
});
dispatch_sync(serialQue, ^{
NSLog(@"串行队列 同步任务 2");
});
dispatch_sync(serialQue, ^{
NSLog(@"串行队列 同步任务 3");
});
NSLog(@"步骤2");
下面再来分析下锁住的原因:iOS中函数默认都是运行在主线程中,而主线程所在的主队列是串行队列;那么上述函数中在主队列中有一个任务(viewDidLoad)开始执行,当执行到dispatch_sync
的时候串行队列中有多了个同步任务,那么上一个任务(viewDidLoad)等待该任务执行完才能接着执行,但是因为是同步线程,dispatch_sync的任务需要等到上一个任务执行完成才能执行;这样就造成两个任务相互等待对方执行完成,从而造成死锁;解决方式有以下两种:
a,主队列中异步执行:
NSLog(@"步骤1");
dispatch_queue_t mianQue = dispatch_get_main_queue();
dispatch_async(mianQue, ^{
NSLog(@"步骤2");
});
NSLog(@"步骤3");
//2019-09-28 10:16:07.384373+0800 testThread[20853:156963] 步骤1
//2019-09-28 10:16:07.384633+0800 testThread[20853:156963] 步骤2
//2019-09-28 10:16:07.384782+0800 testThread[20853:156963] 步骤3
b, 新队列中执行:
NSLog(@"步骤1");
dispatch_queue_t que = dispatch_queue_create("kkk", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(que, ^{//异步这里就不再测试
NSLog(@"步骤2");
});
NSLog(@"步骤3");
//2019-09-28 10:16:07.384373+0800 testThread[20853:156963] 步骤1
//2019-09-28 10:16:07.384633+0800 testThread[20853:156963] 步骤2
//2019-09-28 10:16:07.384782+0800 testThread[20853:156963] 步骤3
**
注意:使用sync在当前串行队列中添加任务,会是当前队列产生死锁;
**
NSLog(@"步骤1");
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"步骤2");
});
NSLog(@"步骤3");
- (void)test{
NSLog(@"%s",__func__);
}
2019-09-28 10:56:17.360642+0800 testThread[27068:210230] 步骤1
2019-09-28 10:56:17.360920+0800 testThread[27068:210230] 步骤3
2019-09-28 10:56:17.361117+0800 testThread[27068:210325] 步骤2
从上面打印可以看出并没有执行test()方法;
NSLog(@"步骤1");
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
NSLog(@"步骤2");
});
NSLog(@"步骤3");
- (void)test{
NSLog(@"%s",__func__);
CFRunLoopStop(CFRunLoopGetCurrent());
}
2019-09-28 11:03:08.085945+0800 testThread[28275:221117] 步骤1
2019-09-28 11:03:08.086135+0800 testThread[28275:221117] 步骤3
2019-09-28 11:03:08.086765+0800 testThread[28275:221198] -[ViewController test]
2019-09-28 11:03:08.087350+0800 testThread[28275:221198] 步骤2
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"步骤0");
NSThread *curentThres = [[NSThread alloc]initWithBlock:^{
NSLog(@"步骤1");
}];
[curentThres start];
NSLog(@"步骤2");
[self performSelector:@selector(test) onThread:curentThres withObject:nil waitUntilDone:YES];
}
运行结果如下:
2019-09-28 11:16:04.951807+0800 testThread[30020:237237] 步骤0
2019-09-28 11:16:04.952187+0800 testThread[30020:237237] 步骤2
2019-09-28 11:16:04.954974+0800 testThread[30020:237393] 步骤1
2019-09-28 11:16:04.980856+0800 testThread[30020:237237] *** Terminating app due to uncaught exception 'NSDestinationInvalidException', reason: '*** -[ViewController performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
原因是在执行performSelector的时候curentThres在执行完一个任务后已经销毁了,解决方法也是runloop:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"步骤0");
NSThread *curentThres = [[NSThread alloc]initWithBlock:^{
NSLog(@"步骤1");
//不添加下面这一句会崩溃
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[curentThres start];
NSLog(@"步骤2");
[self performSelector:@selector(test) onThread:curentThres withObject:nil waitUntilDone:YES];
}
多个并发的任务执行完成后再执行另一个任务,最经典的就是多线程下载的应用;使用很简单;我们直接看源码实例:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t concurrentQue = dispatch_queue_create("com.lym.concurrentGroup", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"步骤0");
dispatch_group_async(group,concurrentQue, ^{
[NSThread sleepForTimeInterval:2.0f];
NSLog(@"并发队列组 异步任务 1");
});
dispatch_group_async(group,concurrentQue, ^{
[NSThread sleepForTimeInterval:2.0f];
NSLog(@"并发队列组 异步任务 2");
});
dispatch_group_async(group,concurrentQue, ^{
[NSThread sleepForTimeInterval:2.0f];
NSLog(@"并发队列组 异步任务 3");
});
dispatch_group_notify(group, concurrentQue, ^{
NSLog(@"全部执行完成");
});
NSLog(@"步骤2");
2019-09-28 11:31:12.132972+0800 testThread[32679:259275] 步骤0
2019-09-28 11:31:12.133151+0800 testThread[32679:259275] 步骤2
2019-09-28 11:31:14.135128+0800 testThread[32679:259330] 并发队列组 异步任务 2
2019-09-28 11:31:14.135159+0800 testThread[32679:259331] 并发队列组 异步任务 3
2019-09-28 11:31:14.135147+0800 testThread[32679:259329] 并发队列组 异步任务 1
2019-09-28 11:31:14.135595+0800 testThread[32679:259331] 全部执行完成
#import "LYMBaseThreadTast.h"
@interface LYMBaseThreadTast ()
@property(nonatomic,assign)NSInteger cont;
@end
@implementation LYMBaseThreadTast
/*****售票问题******/
-(void)scaleTicket{
int count = (int)_cont;
count = count - 1;;
sleep(.3);
self.cont = count;
NSLog(@"还剩%ld currenthread:%@",_cont,[NSThread currentThread]);
}
-(void)scaleTicketsTask{
self.cont = 20;
dispatch_queue_t scaleTicketQue = dispatch_queue_create("com.lym.scaleTicket", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(scaleTicketQue, ^{
for (int i = 10; i > 0; i--) {
[self scaleTicket];
}
});
dispatch_async(scaleTicketQue, ^{
for (int i = 5; i > 0; i--) {
[self scaleTicket];
}
});
dispatch_async(scaleTicketQue, ^{
for (int i = 5; i > 0; i--) {
[self scaleTicket];
}
});
}
/*****end****/
@end
输出:
2019-09-28 11:58:44.783825+0800 testThread[36523:296048] 还剩19 currenthread:<NSThread: 0x60000065e400>{number = 3, name = (null)}
2019-09-28 11:58:44.783831+0800 testThread[36523:296045] 还剩19 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.783932+0800 testThread[36523:296046] 还剩19 currenthread:<NSThread: 0x6000006691c0>{number = 4, name = (null)}
2019-09-28 11:58:44.784113+0800 testThread[36523:296046] 还剩18 currenthread:<NSThread: 0x6000006691c0>{number = 4, name = (null)}
2019-09-28 11:58:44.784113+0800 testThread[36523:296048] 还剩18 currenthread:<NSThread: 0x60000065e400>{number = 3, name = (null)}
2019-09-28 11:58:44.784116+0800 testThread[36523:296045] 还剩18 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.784242+0800 testThread[36523:296046] 还剩17 currenthread:<NSThread: 0x6000006691c0>{number = 4, name = (null)}
2019-09-28 11:58:44.784246+0800 testThread[36523:296048] 还剩17 currenthread:<NSThread: 0x60000065e400>{number = 3, name = (null)}
2019-09-28 11:58:44.784254+0800 testThread[36523:296045] 还剩16 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.784345+0800 testThread[36523:296046] 还剩15 currenthread:<NSThread: 0x6000006691c0>{number = 4, name = (null)}
2019-09-28 11:58:44.785462+0800 testThread[36523:296048] 还剩14 currenthread:<NSThread: 0x60000065e400>{number = 3, name = (null)}
2019-09-28 11:58:44.785538+0800 testThread[36523:296045] 还剩13 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.785745+0800 testThread[36523:296046] 还剩12 currenthread:<NSThread: 0x6000006691c0>{number = 4, name = (null)}
2019-09-28 11:58:44.785979+0800 testThread[36523:296048] 还剩11 currenthread:<NSThread: 0x60000065e400>{number = 3, name = (null)}
2019-09-28 11:58:44.786492+0800 testThread[36523:296045] 还剩10 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.786959+0800 testThread[36523:296045] 还剩9 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.787282+0800 testThread[36523:296045] 还剩8 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.791496+0800 testThread[36523:296045] 还剩7 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.791644+0800 testThread[36523:296045] 还剩6 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
2019-09-28 11:58:44.791770+0800 testThread[36523:296045] 还剩5 currenthread:<NSThread: 0x60000066cc80>{number = 5, name = (null)}
从上面的例子里可以看到,当多个线程同时读写一个变量的时候就会出现异常;
OSSpinLock
将上述方法修改如下:
#import "LYMOSSpinLockThreadTast.h"
#import
@interface LYMOSSpinLockThreadTast ()
@property(nonatomic,assign)OSSpinLock lock;
@end
@implementation LYMOSSpinLockThreadTast
-(instancetype)init{
if (self=[super init]) {
self.lock = OS_SPINLOCK_INIT;
}
return self;
}
/*****售票问题 自旋锁解决方式******/
-(void)scaleTicket{
OSSpinLockLock(&_lock);//自旋锁加锁
[super scaleTicket];
OSSpinLockUnlock(&_lock);//自旋锁解锁
}
/*****end****/
@end
/*****end****/
输出:
2019-12-01 13:00:45.190729+0800 还剩19 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.191271+0800 还剩18 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.191511+0800 还剩17 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.193041+0800 还剩16 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.193231+0800 还剩15 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.193416+0800 还剩14 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.193586+0800 还剩13 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.193772+0800 还剩12 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.194042+0800 还剩11 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.198458+0800 还剩10 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a115c0>{number = 3, name = (null)}
2019-12-01 13:00:45.199288+0800 还剩9 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a2af40>{number = 4, name = (null)}
2019-12-01 13:00:45.199431+0800 还剩8 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a2af40>{number = 4, name = (null)}
2019-12-01 13:00:45.199560+0800 还剩7 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a2af40>{number = 4, name = (null)}
2019-12-01 13:00:45.199700+0800 还剩6 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a2af40>{number = 4, name = (null)}
2019-12-01 13:00:45.199816+0800 还剩5 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a2af40>{number = 4, name = (null)}
2019-12-01 13:00:45.203833+0800 还剩4 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a10080>{number = 5, name = (null)}
2019-12-01 13:00:45.204001+0800 还剩3 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a10080>{number = 5, name = (null)}
2019-12-01 13:00:45.204128+0800 还剩2 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a10080>{number = 5, name = (null)}
2019-12-01 13:00:45.204258+0800 还剩1 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a10080>{number = 5, name = (null)}
2019-12-01 13:00:45.204381+0800 还剩0 scaleTicket_OSSpinLock currenthread:<NSThread: 0x600000a10080>{number = 5, name = (null)}
自旋锁实现大致是一个循环不断去判断是不是完成任务,就是说线程会一直占用cpu资源即:忙等;假如这个等待的线程是级别比较高的线程,那么线程调度时候就会导致其他线程无法执行;及线程等级反转。
所以苹果已经不推荐使用自旋锁;因此推荐使用os_unfair_lock:
#import "LYMOSUnfairLockThreadTast.h"
#import
@interface LYMOSUnfairLockThreadTast ()
@property (assign, nonatomic) os_unfair_lock ticketLock;
@end
@implementation LYMOSUnfairLockThreadTast
/*****售票问题 os_unfair_lock #import ******/
-(instancetype)init{
if (self=[super init]) {
self.ticketLock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
-(void)scaleTicket{
os_unfair_lock_lock(&_ticketLock);
[super scaleTicket];
os_unfair_lock_unlock(&_ticketLock);//解锁
}
/*****end****/
@end
#import "LYMOSPThreadTast.h"
#import
@interface LYMOSPThreadTast ()
@property(nonatomic,assign)pthread_mutex_t mutex_lock;//互斥锁
@end
@implementation LYMOSPThreadTast
/*****售票问题 os_unfair_lock #import ******/
-(instancetype)init{
if (self=[super init]) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);//普通锁
pthread_mutex_init(&(_mutex_lock), &attr);//第二个参数默认可以为NULL
}
return self;
}
-(void)scaleTicket{
pthread_mutex_lock(&(_mutex_lock));
[super scaleTicket];
pthread_mutex_unlock(&(_mutex_lock));//解锁
}
-(void)dealloc{
pthread_mutex_destroy(&(_mutex_lock));
}
/*****end****/
@end
上述代码中加锁的代码中如果有调用了使用同一把锁加锁的代码,就会造成死锁,这时候应该使用递归锁pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//递归锁
;
在加锁与解锁成对的线程中,递归锁允许对同一把锁重复加锁
//
// LYMNSCordition.m
// testThread
//
// Created by ymluo on 2019/12/6.
// Copyright © 2019 ymluo. All rights reserved.
//
#import "LYMNSCordition.h"
@interface LYMNSCordition()
@property (strong, nonatomic)NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation LYMNSCordition
- (instancetype)init
{
if (self = [super init]) {
//当内部的d条件值等于1的时候,才能加锁
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
// [[[NSThread alloc] initWithTarget:self selector:@selector(_one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
}
- (void)__two
{
[_condition lock];
NSLog(@"__fnc:%s 2",__func__);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"__fnc:%s 2 sleep",__func__);
sleep(2);
[self.condition signal];
NSLog(@"__fnc:%s 2 sleep untile",__func__);
});
[_condition wait];
[_condition unlock];
NSLog(@"__fnc:%s 2 end function",__func__);
}
- (void)__one
{
[_condition lock];
NSLog(@"__fnc:%s 1",__func__);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"__fnc:%s 1 sleep",__func__);
sleep(2);
[self.condition signal];
NSLog(@"__fnc:%s 1 sleep untile",__func__);
});
[_condition wait];
[_condition unlock];
[self __two];
}
@end
// 输出
2019-12-06 09:01:21.794134+0800 __fnc:-[LYMNSCordition __one] 1
2019-12-06 09:01:21.806155+0800 __fnc:-[LYMNSCordition __one]_block_invoke 1 sleep
2019-12-06 09:01:23.806536+0800 __fnc:-[LYMNSCordition __one]_block_invoke 1 sleep untile
2019-12-06 09:01:23.806533+0800 __fnc:-[LYMNSCordition __two] 2
2019-12-06 09:01:23.808100+0800 __fnc:-[LYMNSCordition __two]_block_invoke 2 sleep
2019-12-06 09:01:25.809708+0800 __fnc:-[LYMNSCordition __two]_block_invoke 2 sleep untile
2019-12-06 09:01:25.809708+0800 __fnc:-[LYMNSCordition __two] 2 end function
//
// LYMNsconditionLock.m
// testThread
//
// Created by ymluo on 2019/12/6.
// Copyright © 2019 ymluo. All rights reserved.
//
#import "LYMNsconditionLock.h"
@interface LYMNsconditionLock()
@property (strong, nonatomic)NSConditionLock *conditionLock;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation LYMNsconditionLock
- (instancetype)init
{
if (self = [super init]) {
//当内部的d条件值等于1的时候,才能加锁
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__third) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(_one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
}
/**
因为加锁的内部条件是condition等于1 所以该线程的方法会等待线程2的__two执行完毕
*/
- (void)_one
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__fnc:%s 2",__func__);
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
/**
因为加锁的内部条件是condition等于1 所以该线程的方法会最先执行
*/
- (void)__two
{
[self.conditionLock lockWhenCondition:1];
NSLog(@"__fnc:%s 1",__func__);
dispatch_async(dispatch_get_main_queue(), ^{
});
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
/**
因为加锁的内部条件是condition等于1 所以该线程的方法会等到 1和2都执行完毕且condition的值等于开始执行下一个 方法
*/
- (void)__third{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__fnc:%s 3",__func__);
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
@end
2019-12-06 09:07:16.887674+0800 __fnc:-[LYMNsconditionLock __two] 1
2019-12-06 09:07:17.893224+0800 __fnc:-[LYMNsconditionLock _one] 2
2019-12-06 09:07:18.898025+0800 __fnc:-[LYMNsconditionLock __third] 3
信号量可以控制同时最大并发数,也可当做线程锁使用;
#pragma mark - 控制同时执行线程数(控制最大并发数)
- (void)otherTestMaxThred{
for (NSInteger i = 30; i > 0; i--) {
NSThread *thread = [[NSThread currentThread]initWithTarget:self selector:@selector(__thestThred:) object:@(i)];
[thread start];
}
}
- (void)__thestThred:(id)obj{
// 如果信号量的值 > 0,那么就让信号量的值减一,且继续向下执行;
// 如果 <= o,则等待(线程休眠等待),直到dispatch_semaphore_signal()让信号量的值 +1,就让信号量的值 -1,然后继续执行代码。
dispatch_semaphore_wait(_dispatchSemapthore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"thread:%@ i:%@",[NSThread currentThread],obj);
// 让信号量的值 +1;
dispatch_semaphore_signal(_dispatchSemapthore);
}
#import "LYMSemphoreTest.h"
@interface LYMSemphoreTest ()
@property (nonatomic, strong) dispatch_semaphore_t dispatchSemapthore; //!<信号量
@end
@implementation LYMSemphoreTest
-(instancetype)init{
if (self = [super init]) {
self.dispatchSemapthore = dispatch_semaphore_create(1);
}
return self;
}
-(void)scaleTicket{
// 如果信号量的值 > 0,那么就让信号量的值减一,且继续向下执行;
// 如果 <= o,则等待(线程休眠等待),直到dispatch_semaphore_signal()让信号量的值 +1,就让信号量的值 -1,然后继续执行代码。
dispatch_semaphore_wait(_dispatchSemapthore, DISPATCH_TIME_FOREVER);
[super scaleTicket];
// 让信号量的值 +1;
dispatch_semaphore_signal(_dispatchSemapthore);
}
@end
2019-12-08 11:43:20.307289+0800 还剩19 currenthread:<NSThread: 0x60000201cd40>{number = 3, name = (null)}
2019-12-08 11:43:20.307705+0800 还剩18 currenthread:<NSThread: 0x60000202cec0>{number = 4, name = (null)}
。。。。。
2019-12-08 11:43:20.320493+0800 还剩1 currenthread:<NSThread: 0x60000201cd40>{number = 3, name = (null)}
2019-12-08 11:43:20.320789+0800 还剩0 currenthread:<NSThread: 0x60000201cd40>{number = 3, name = (null)}
/**
线程中等待子线程执行返回后继续执行任务
*/
- (void)otherTestThred{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self __thestThred1:@(0)];
});
}
- (void)__thestThred1:(id)obj{
NSLog(@"===begin thread:%@ i:%@",[NSThread currentThread],obj);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"thread 1:%@ i:%@",[NSThread currentThread],obj);
sleep(2);
NSLog(@"thread 2:%@ i:%@",[NSThread currentThread],obj);
// 让信号量的值 +1;
dispatch_semaphore_signal(self.dispatchSemapthore);
});
dispatch_semaphore_wait(_dispatchSemapthore, DISPATCH_TIME_FOREVER);
NSLog(@"===end thread:%@ i:%@",[NSThread currentThread],obj);
输出:
2019-12-08 11:48:44.393387+0800 ===begin thread:<NSThread: 0x6000021eee40>{number = 3, name = (null)} i:0
2019-12-08 11:48:44.393922+0800 thread 1:<NSThread: 0x6000021d5c00>{number = 4, name = (null)} i:0
2019-12-08 11:48:46.396430+0800 thread 2:<NSThread: 0x6000021d5c00>{number = 4, name = (null)} i:0
2019-12-08 11:48:46.396631+0800 ===end thread:<NSThread: 0x6000021eee40>{number = 3, name = (null)} i:0
是对mutex递归锁的封装,参数self,在底层中将其地址作为key去取出对应的锁;
@synchronized (self) {
// Task
}
性能由高到低依次如下:
os_unfair_lock:性能最高,
OSSpinLock:预计线程等待时间很短的时候;cpu资源足够;很少发生竞争;
dispatch_semaphore:
pthread_mutex:
dispatch_queue(DISPATCH_QUEUE_SERIAL);//同步队列
NSLock
NSCondition
pthread_mutex():recursive//递归锁
NSRecuriseiveLock
NSConditionLock
@synchronized
多读单写:pthread_rwlock 等待的锁会进入 休眠
@interface LYMIOSafeTest ()
//@property (nonatomic, strong) dispatch_semaphore_t dispatchSemapthore; //!<信号量
//@property (nonatomic, copy) NSString *filePath; //!<<#注释#>
@property (nonatomic, assign) pthread_rwlock_t rwLock; //!<信号量
@property (nonatomic, copy) NSString *filePath; //!<<#注释#>
@end
@implementation LYMIOSafeTest
-(instancetype)init{
if (self = [super init]) {
pthread_rwlock_init(&(_rwLock), NULL);
NSString *documetPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
self.filePath = [documetPath stringByAppendingPathComponent:@"testFile.txt"];
}
return self;
}
-(void)otherTest{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 5; i++) {
dispatch_async(queue, ^{
[self __writeileThred:@(i)];
});
dispatch_async(queue, ^{
[self __readFileThred:@(i)];
});
}
}
- (void)__readFileThred:(id)obj{
pthread_rwlock_rdlock(&_rwLock);
if ([[NSFileManager defaultManager] fileExistsAtPath:_filePath]) {
NSString *resultStr = [NSString stringWithContentsOfFile:_filePath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"__readFileThred=====:str= %@",resultStr);
}
sleep(1);
pthread_rwlock_unlock(&_rwLock);
}
- (void)__writeileThred:(id)obj{
pthread_rwlock_wrlock(&_rwLock);
// 字符串读取的方法
if ([obj integerValue] == 0) {
if ([[NSFileManager defaultManager] fileExistsAtPath:_filePath]) {
[[NSFileManager defaultManager] removeItemAtPath:_filePath error:nil];
}
}
if (![[NSFileManager defaultManager] fileExistsAtPath:_filePath]) {
[[NSFileManager defaultManager] createFileAtPath:_filePath contents:nil attributes:nil];
}
NSFileHandle *handler = [NSFileHandle fileHandleForUpdatingAtPath:_filePath];
[handler seekToEndOfFile];
[handler writeData:[[NSString stringWithFormat:@"\n线程开始写入数据:%@",obj] dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"__writeileThred thread:%@",[NSThread currentThread]);
sleep(1);
pthread_rwlock_unlock(&_rwLock);
}
-(void)dealloc{
pthread_rwlock_destroy(&_rwLock);
}
输出:
2019-12-08 14:33:15.376561+0800 __writeileThred thread:<NSThread: 0x600002b4ddc0>{number = 3, name = (null)}
2019-12-08 14:33:16.382122+0800 __readFileThred=====:str=
线程开始写入数据:0
2019-12-08 14:33:16.382244+0800 __readFileThred=====:str=
线程开始写入数据:0
2019-12-08 14:33:17.385887+0800 __writeileThred thread:<NSThread: 0x600002b7d640>{number = 4, name = (null)}
2019-12-08 14:33:18.392534+0800 __writeileThred thread:<NSThread: 0x600002b4dfc0>{number = 5, name = (null)}
2019-12-08 14:33:19.395925+0800 __readFileThred=====:str=
线程开始写入数据:0
线程开始写入数据:1
线程开始写入数据:2
2019-12-08 14:33:20.401841+0800 __writeileThred thread:<NSThread: 0x600002b4df40>{number = 6, name = (null)}
2019-12-08 14:33:21.409091+0800 __readFileThred=====:str=
线程开始写入数据:0
线程开始写入数据:1
线程开始写入数据:2
线程开始写入数据:3
2019-12-08 14:33:22.413197+0800 __writeileThred thread:<NSThread: 0x600002b7c640>{number = 7, name = (null)}
2019-12-08 14:33:23.417572+0800 __readFileThred=====:str=
线程开始写入数据:0
线程开始写入数据:1
线程开始写入数据:2
线程开始写入数据:3
线程开始写入数据:4
从输出中可以看出读可以多个同时执行,而写同时只能存在一个;