1.信号量的使用 wait signal 线程同步的方法
semaphore_ = dispatch_semaphore_create(1);
dispatch_semaphore_t semaphore_;
{
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timers_[name];
if (timer) {
dispatch_source_cancel(timer);
[timers_ removeObjectForKey:name];
}
dispatch_semaphore_signal(semaphore_);
}
2.iOS的多线程有哪几种?
pthread、NSThread、GCD、NSOperation。后边3种都是基于pthread封装的。
3.如何理解同步、异步、串行、并发
dispatch_sync 同步:在当前线程执行任务,不具备开启新线程的能力;立刻在当前线程执行任务,执行完毕才能往下走。
dispatch_async 异步:在新线程执行任务,具备开启新线程的能力;不要求立刻在当前线程执行任务
串行:一个任务执行完毕后,再执行下一个任务。
并发:多个任务并发(同时)执行。
总结:同步异步研究的能不能开启新的线程。
串行并发研究的是任务的执行方式。
4.各种队列的执行结果
并发队列 手动创建的串行队列 主队列
同 没有开启新线程 没有开启新线程 没有开启新线程
步 串行执行任务 串行执行任务 串行执行任务
异 有开启新线程 有开启新线程 没有开启新线程
步 并发执行任务 串行执行任务 串行执行任务
总结:sync和主队列 一定是在当前线程串行执行任务。
使用sync函数往当前串行队列中添加任务,会卡主当前的串行队列(产生死锁)。
sync+当前串行队列
5.下面代码打印结果是什么?为什么?
13;performSelector:@selector(test) withObject:nil afterDelay:.0本质是往runloop中添加定时器,子线程默认没有启动Runloop.
- (void)test2
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
// 这句代码的本质是往Runloop中添加定时器
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
//[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];这句代码可以省略,因为runloop中已经添加了定时器了,runloop就不会退出了。
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
}
- (void)test
{
NSLog(@"2");
}
6.下面2个方法的区别?
performSelector:@selector(test) withObject:nil afterDelay:.0 本质:是向当前runloop添加了一个定时器。
performSelector:@selector(test) withObject:nil = [self test]; 本质是objc_msgsend(self, @selector(test) )
7.如果想看NSArray.m怎么办?
(1)GNUstep不是官方源码,但还是具有一定的参考价值。GNUstep是个组织希望把所有代码都开源。http://www.gnustep.org/resources/downloads.php
(2)打断点一步一步跟进去看汇编。
8.下面代码打印结果是什么?为什么?
打印完1 后崩溃,start后会立刻执行block内的任务,然后在执行test,block和test任务不可能同时执行,当前线程没有runloop执行完任务后会立刻退出,此时向线程中添加test任务会崩溃。
- (void)test
{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
9.dispatch_group_async使用中dispatch_group_notify可以使用多个吗?可以。
dispatch_group_async使用DISPATCH_QUEUE_CONCURRENT,dispatch_group_notify可以使用dispatch_get_main_queue?可以。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
// 添加异步任务
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务1-%@", [NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务2-%@", [NSThread currentThread]);
}
});
// 等前面的任务执行完毕后,会自动执行这个任务
// dispatch_group_notify(group, queue, ^{
// dispatch_async(dispatch_get_main_queue(), ^{
// for (int i = 0; i < 5; i++) {
// NSLog(@"任务3-%@", [NSThread currentThread]);
// }
// });
// });
// dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// for (int i = 0; i < 5; i++) {
// NSLog(@"任务3-%@", [NSThread currentThread]);
// }
// });
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务3-%@", [NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务4-%@", [NSThread currentThread]);
}
});
}
10.static NSString *str = [NSString stringWithFormat:@"123"]为何会编译不过?
static NSString *str = [NSString stringWithFormat:@"123"];
static意思是在编译时的值就得确定,而右边的值相当于是一个函数值是在在运行时才能确定,我们可以把这种情况写在dispatch_once里边接口。
11.多线程的安全隐患?
多个线程访问同一块资源,如同一个对象、同一个变量、同一个文件,易引发数据错乱问题。
12.线程阻塞有2种情况?
一种是线程休眠,不占用CPU资源,另外一种是线程忙等,一直占用CPU资源。
忙等的意思是:while(1) ; 这种其实死循环会一直占用CPU自愿的。
13.iOS中的线程同步方案有哪些?
(1)OSSpinLockLock 自旋锁
优点:效率高。主要是相对于需要唤醒线程中runloop的所来说的。
缺点:不安全。它会一直占用着CPU资源,优先级低的线程就无法释放锁。
进程、线程的时间片轮转调度算法:优先级高的并发执行,调度给这个高优先级的进程或线程时间就越多。
线程优先级:
thread1:优先级比较高 20ms
thread2:优先级比较中 15ms
thread3:优先级比较低 10ms
#import <libkern/OSAtomic.h>
//初始化
self.lock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&_lock);
// 解锁
OSSpinLockUnlock(&_lock);
(2)os_unfair_lock -汇编分析是互斥锁
安全:会处于休眠状态,并非忙等。
unfair 不公平的
#import <os/lock.h>
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&lock);
os_unfair_lock_unlock(&lock);
死锁:执行lock未执行unlock或者unlock地址错误,另外一个线程永远进不来。
(3)pthread_mutex 普通锁PTHREAD_MUTEX_DEFAULT
mutex互斥锁:等待锁的线程会处于休眠状态。
#import <pthread.h>
初始化锁
@property (assign, nonatomic) pthread_mutex_t moneyMutex;
initMutex:(pthread_mutex_t *)mutex{
// 静态初始化
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(mutex, &attr);//pthread_mutex_init(mutex, NULL);等价于DEFAULT属性
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
使用锁
pthread_mutex_lock(&_moneyMutex);
pthread_mutex_unlock(&_moneyMutex);
销毁锁
dealloc {
pthread_mutex_destroy(&_moneyMutex);
}
(4)pthread_mutex 递归锁:允许同一个线程对一把锁进行重复加锁 PTHREAD_MUTEX_RECURSIVE
- (void)__initMutex:(pthread_mutex_t *)mutex {
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
如果一个线程在执行,其他线程就会等待。
- (void)otherTest {
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 10) {
count++;
[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc {
pthread_mutex_destroy(&_mutex);
}
(5)pthread_mutex 条件锁
使用场景:用于线程之间的依赖,线程A需要等待线程B做完再回到线程A做事情。
#import <pthread.h>
@interface MutexDemo3()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation MutexDemo3
- (instancetype)init
{
if (self = [super init]) {
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(&_mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
// 初始化条件
pthread_cond_init(&_cond, NULL);
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待条件(进入休眠即阻塞停在这里,放开mutex;被唤醒(执行signal)后,如果没有加锁,会再次对mutex加锁,如果加锁了就等待,被放开后会再加锁)
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
NSLog(@"删除了元素");
pthread_mutex_unlock(&_mutex);
}
// 线程2
// 往数组中添加元素
- (void)__add
{
pthread_mutex_lock(&_mutex);
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 激活一个等待该条件的线程
pthread_cond_signal(&_cond); //注意这句代码写在这里和pthread_mutex_unlock后的区别?写在这里的话,上面wait后的代码会等pthread_mutex_unlock执行完才执行;写在pthread_mutex_unlock后,上面wait后的代码会立刻执行。
// 激活所有等待该条件的线程
// pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
(6)NSLock
NSLock是对mutex普通锁的封装,没区别仅封装而已。
GNUStep可以看到源码。
(7)NSRecursivelock
NSRecursivelock对mutex递归锁的封装
(8)NSCondition
NSCondition是对mutex和cond两个的封装
@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation NSConditionDemo
- (instancetype)init {
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest {
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove {
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}
// 线程2
// 往数组中添加元素
- (void)__add {
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
// 广播
// [self.condition broadcast];
[self.condition unlock];
}
@end
(9)NSConditionLock
NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值。
使用场景:多线程下的任务依赖。与cond区别是:A->B->A cond 在A中等B执行还会回到A,即会不会回到A中的区别。
@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end
@implementation NSConditionLockDemo
- (instancetype)init
{
if (self = [super init]) {
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1]; //NSConditionLock alloc] init 默认是0
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}
- (void)__one
{
[self.conditionLock lock]; //不管默认值是几都会进来
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three
{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlock];
}
@end
(10)直接使用GCD的串行队列,也是可以实现线程同步的
@interface SerialQueueDemo()
@property (strong, nonatomic) dispatch_queue_t ticketQueue;
@property (strong, nonatomic) dispatch_queue_t moneyQueue;
@end
@implementation SerialQueueDemo
- (instancetype)init {
if (self = [super init]) {
self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
self.moneyQueue = dispatch_queue_create("moneyQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)__drawMoney {
dispatch_sync(self.moneyQueue, ^{
[super __drawMoney];
});
}
- (void)__saveMoney {
dispatch_sync(self.moneyQueue, ^{
[super __saveMoney];
});
}
- (void)__saleTicket {
dispatch_sync(self.ticketQueue, ^{
[super __saleTicket];
});
}
@end
(11)semaphore -1 +1
设置线程最大并发量,信号量>1即可;线程同步=1,信号量=1即可。
@interface SemaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
@property (strong, nonatomic) dispatch_semaphore_t moneySemaphore;
@end
@implementation SemaphoreDemo
- (instancetype)init {
if (self = [super init]) {
self.semaphore = dispatch_semaphore_create(5);
self.ticketSemaphore = dispatch_semaphore_create(1);
self.moneySemaphore = dispatch_semaphore_create(1);
}
return self;
}
- (void)__drawMoney {
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __drawMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__saveMoney {
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __saveMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__saleTicket {
dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
dispatch_semaphore_signal(self.ticketSemaphore);
}
- (void)otherTest {
for (int i = 0; i < 20; i++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
}
}
// 线程10、7、6、9、8
- (void)test {
// 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
// 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); //-1
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 让信号量的值+1
dispatch_semaphore_signal(self.semaphore); //+1
}
@end
(12)@synchronized 递归锁
源码查看:objc4中的objc-sync.mm文件
@synchronized是对mutex递归锁的封装,所以可以嵌套使用。
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
缺点:性能差,封装了哈希表,又将mutex封装成了C++的类
@implementation SynchronizedDemo
//如果synchronized后都使用[self class],所有对象调用都用了一把锁,
//如果synchronized后都使用self,不同对象调用都用了一把锁,
- (void)__drawMoney
{
@synchronized([self class]) {
[super __drawMoney];
}
}
- (void)__saveMoney
{
@synchronized([self class]) { // objc_sync_enter
[super __saveMoney];
} // objc_sync_exit
}
- (void)__saleTicket
{
static NSObject *lock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = [[NSObject alloc] init];
});
@synchronized(lock) {
[super __saleTicket];
}
}
- (void)otherTest
{
@synchronized([self class]) {
NSLog(@"123");
[self otherTest];
}
}
@end
14.锁的性能对比:C > OC
dispatch_semaphore、pthread_mutex使用较多
性能从高到低排序
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
15.保住线程的命为什么要用runloop,用强指针不就好了嘛?
用强指针虽然可以是线程在内存中,但是还是它还是不能用,因为还需要让线程保持激活状态。
16.自旋锁、互斥锁使用场景有哪些?
iOS自旋锁已经基本不能使用,基本都是互斥锁了。
什么情况使用自旋锁比较划算?
(1)预计线程等待锁的时间很短;
(2)加锁的代码(临界区)经常被调用,但竞争情况很少发生;
(3)CPU资源不紧张;
(4)多核处理器.
什么情况使用互斥锁比较划算?
(1)预计线程等待锁的时间较长;
(2)临界区有IO操作;
(3)临界区代码复杂或者循环量大;
(4)临界区竞争非常激烈;
(5)单核处理器。
17.atomic 一般mac使用多
tomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
atomic和monatomic区别?
-(void)setIcon1:(UIImage *)icon1
{ @synchronized (self) {//同步代码块
if(_icon1 != icon1) {
[_icon1 release];
_icon1 = [icon1 retain];
}
}
}
修饰基本数据类型没区别;
atomic修饰oc对象多线程下不会崩溃,但结果无法预知。
monatomic修饰oc对象多线程下会崩溃。
atomic原子性并不能保证多线程安全,只是能保证数据的完整性
这个完整性体现在:使用者总能取到完整的值
无法预知的原因:虽然atomic保证了number属性线程安全了,但是并不能保证temp变量的线程安全,又因为是多线程的,所以有可能同时执行多次 int temp = _number+1;才执行一次 _number = temp;导致结果每次都不同,而且结果不可预知。
崩溃原因:因为是多线程,且没有加锁保护,所以在一个线程走到[_name release]后,可能在另一个线程又一次去释放,这时候造成崩溃。
18.实现多读单写的方案有哪些?
思考如何实现以下场景
同一时间,只能有1个线程进行写的操作
同一时间,允许有多个线程进行读的操作
同一时间,不允许既有写的操作,又有读的操作
上面的场景就是典型的“多读单写”,经常用于文件等数据的读写操作,iOS中的实现方案有
(1)pthread_rwlock:读写锁
#import <pthread.h>
@interface ViewController ()
@property (assign, nonatomic) pthread_rwlock_t lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
pthread_rwlock_init(&_lock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}
- (void)read {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
- (void)write
{
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
- (void)dealloc
{
pthread_rwlock_destroy(&_lock);
}
@end
(2)dispatch_barrier_async:异步栅栏调用
这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的,
如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果
dispatch_barrier_async(串行, ^{
[self write];
});
等于
dispatch_async(串行, ^{
[self write];
});
dispatch_barrier_async(全局并发, ^{
[self write];
});
等于
dispatch_async(全局并发, ^{
[self write];
});
#import <pthread.h>
@interface ViewController ()
@property (strong, nonatomic) dispatch_queue_t queue;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(self.queue, ^{
[self read];
});
dispatch_async(self.queue, ^{
[self read];
});
dispatch_async(self.queue, ^{
[self read];
});
dispatch_barrier_async(self.queue, ^{
[self write];
});
}
}
- (void)read {
sleep(1);
NSLog(@"read");
}
- (void)write
{
sleep(1);
NSLog(@"write");
}
@end
多线程
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
相关阅读更多精彩内容
- 共17篇博文【白话经典算法系列之十七】 数组中只出现一次的数数组A中,除了某一个数字x之外,其他数字都出现了三次,...
- 01-多线程(概述) 接下来,我们来说一说Java中特有的一个知识技术:多线程。 在说线程这个概念...
- 08-多线程(守护线程) 接下来说一下Thread类中的其他方法。 点进去看一看: 我们试...