OC 学习记录随笔 之多线程

总资料
全是随笔 笔记 与 学习资料。没有规律。

常见多线程方法

在这里插入图片描述
请添加图片描述

  • dispatch_queue_global_t qH = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); 并发的
  • dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_CONCURRENT /*DISPATCH_QUEUE_SERIAL*/); 并发和串行自由控制
  • 因为性能原因,推介使用 dispatch_semaphorepthread_mutex

  • 自旋锁,会在等待中持续占用cpu资源。忙等, 如:OSSpinLock
  • 互斥锁,在等待中不会占用cpu资源,真正的在睡眠中。
  • 自旋锁:atomic、OSSpinLock、dispatch_semaphore_t
  • 互斥锁:pthread_mutex、@ synchronized、NSLock、NSConditionLock 、NSCondition、NSRecursiveLock

线程阻塞

  • 当前串行队列里面 添加sync当前队列的任务 会造成 线程死锁
  • 在主线程同理,因为主线程就是串行队列
  • 在当前串行队列任务未执行完成时,在本队列插入同步任务导致死锁
//    dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_CONCURRENT); //不会死锁
    
    dispatch_queue_t que = dispatch_queue_create("jimboqueue", DISPATCH_QUEUE_SERIAL); //死锁
    
    dispatch_async(que, ^{
    
    
        NSLog(@"1 %@", [NSThread currentThread]);
        
        
        dispatch_sync(que, ^{
    
    
            NSLog(@"2 %@", [NSThread currentThread]);
        });
        NSLog(@"3 %@", [NSThread currentThread]);
    });

perforSelector:withObject:afterDelay

- (void)test{
    
    
    NSLog(@"tset   %s", __func__);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    
    dispatch_queue_global_t q = dispatch_get_global_queue(0, 0);

    dispatch_async(q, ^{
    
    
        NSLog(@"1 %@", [NSThread currentThread]);
        [self performSelector:@selector(test) withObject:nil afterDelay:.0];
        NSLog(@"3 %@", [NSThread currentThread]);
    });
}
//2021-12-21 20:58:07.509883+0800 140_RunLoop_iOS[27151:1282963] 1 <NSThread: 0x600000b19b80>{number = 4, name = (null)}
//2021-12-21 20:58:07.510573+0800 140_RunLoop_iOS[27151:1282963] 3 <NSThread: 0x600000b19b80>{number = 4, name = (null)}

这个方法不会调用-test 方法.

  • 因为performSelector: withObject: afterDelay: 是RunLoop的文件中的,不开源的,通过调用堆栈可以看出。的底层堆栈含有__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__方法,是属于RunLoop的范围,所以在当前线程必须存在RunLoop才会进行调用。但是GCD创建的线程默认是不带RunLoop的。
  • 其他不带afterDelay的方法如:performSelector: withObject: 在objc开源代码里面可以看出是直接调用的的objcmsgSend(), 所以不会受影响。
  • NSTimer 的所有方法都是runloop的,知识区别在于有没有捕获target

解决方法:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    
    dispatch_queue_global_t q = dispatch_get_global_queue(0, 0);

    dispatch_async(q, ^{
    
    
        NSLog(@"1 %@", [NSThread currentThread]);
        [self performSelector:@selector(test) withObject:nil afterDelay:.0];
        NSLog(@"3 %@", [NSThread currentThread]);
        
        //runLoop 添加任务和启动
        //[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode]; 可以不用添加任务,因为afterDelay dtimer已经被加入到任务中
        [[NSRunLoop currentRunLoop] run];
    });
}

备注:afterDelay如果不是RunLoop中的话,怎么保证timer几秒后触发的时候,线程不会被销毁呢,只有用RunLoop来保活线程。

各个类型的代码

  • 递归锁只能在同一线程内递归使用,
  • 不同线程内会进行枷锁

os_spin_lock

自旋锁会存在优先级反转,已经不支持了,废弃了

os_unfair_lock

#import <os/lock.h>

//声明
@property (assign, nonatomic) os_unfair_lock fairLock;

//初始化
self.fairLock = OS_UNFAIR_LOCK_INIT;

{
    
    
	os_unfair_lock_lock(&_fairLock);
	//执行代码
	os_unfair_lock_unlock(&_fairLock);
}

Pthread_mutex_t

/**
#define PTHREAD_MUTEX_NORMAL        0 //普通锁. 不能递归,不然会死锁
#define PTHREAD_MUTEX_ERRORCHECK    1
#define PTHREAD_MUTEX_RECURSIVE        2 //递归所
#define PTHREAD_MUTEX_DEFAULT        PTHREAD_MUTEX_NORMAL
*/
    
    //初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    //初始化锁
    pthread_mutex_init(&_mutex, &attr);
    //销毁 attr
    pthread_mutexattr_destroy (&attr);
    {
    
    
        pthread_mutex_lock(&_mutex);
        
        pthread_mutex_unlock(&_mutex);
    }
    
    //销毁锁
    pthread_mutex_destroy(&_mutex);

pthread condition 条件锁

//声明
@property (nonatomic, assign) pthread_mutex_t mutex;
@property (nonatomic, assign) pthread_cond_t cond;
------


//初始化属性
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);

{
    
    
    pthread_mutex_lock(&_mutex);
    
    //等待进入
    pthread_cond_wait(&_cond, &_mutex);
    //激活一个等待线程
    pthread_cond_signal(&_cond);
    //激活所有等待的线程
    pthread_cond_broadcast(&_cond);
    
    
    pthread_mutex_unlock(&_mutex);
}

//销毁锁
pthread_mutex_destroy(&_mutex);
//销毁条件
pthread_cond_destroy(&_cond);

NSLock

是对pthread_mutexPTHREAD_MUTEX_NORMAL封装

@property (nonatomic, strong) NSLock *lo;
---
    self.lo = [[NSLock alloc] init];
   
    
    [self.lo lock];
    //加锁代码
    [self.lo unlock];

NSRecursiveLock

  • NSRecursiveLock 是对pthread_mutexPTHREAD_MUTEX_RECURSIVE封装

NSCondition 和 NSConditionLock

  • NSCondition 是对pthread_mutexpthread_cond_t的 封装
  • NSConditionLock 是对pthread_mutexpthread_cond_t的 封装
  • NSConditionLock 会调用 NSCondition对象

dispatch_queue_t + DISPATCH_QUEUE_SERIAL

串行队列保证线程安全

dispatch_semaphore_t

信号量控制最大并发数量

  • dispatch_semaphore_create(int)

@synchronized

  • @synchronized 是对mutex递归锁的封装
  • 性能最差
// Begin synchronizing on 'obj'. 
// Allocates recursive mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.  
int objc_sync_enter(id obj)
{
    
    
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
    
    
        SyncData* data = id2data(obj, ACQUIRE);
        ASSERT(data);
        data->mutex.lock();
    } else {
    
    
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
    
    
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
    }

    return result;
}

BOOL objc_sync_try_enter(id obj)
{
    
    
    BOOL result = YES;

    if (obj) {
    
    
        SyncData* data = id2data(obj, ACQUIRE);
        ASSERT(data);
        result = data->mutex.tryLock();
    } else {
    
    
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
    
    
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
    }

    return result;
}

自旋锁、互斥锁

虽然自旋锁已经被iOS10被废弃

什么时候使用自旋锁比较划算

  • 预计线程等待锁的时间比较短(这样线程可以不用去睡眠)
  • 加锁的代码(临界区)经常被调用,多条线程同时竞争比较少的。
  • CPU资源不紧张(多核CPU)

互斥锁什么时候划算

  • 预计线程等待锁的时间比较长
  • 单核处理器
  • 临界区有IO操作
  • 代码复杂,循环量比较大,执行时间长
  • 临界区竞争非常激烈

读写锁

需要满足以下要求

  • 同时只有一个线程写
  • 可以有多个线程读
  • 读和写同时只有一个

pthread_rwlock_t

@property(nonatomic, assign) pthread_rwlock_t rwLock;
----
pthread_rwlock_init(&_rwLock, NULL);
    


- (void)read {
    
    
    pthread_rwlock_rdlock(&_rwLock); //读加锁
    NSLog(@"%s", __FUNCTION__);
    pthread_rwlock_unlock(&_rwLock); //通用解锁
}

- (void)write {
    
    
    pthread_rwlock_wrlock(&_rwLock); //写加锁
    NSLog(@"%s", __FUNCTION__);
    pthread_rwlock_unlock(&_rwLock);  //通用解锁
}

- (void)dealloc {
    
    
 pthread_rwlock_destroy(&_rwLock); //结束后调用
}

dispatch_barrier_async()

//必须自定义队列
dispatch_queue_t queue = dispatch_queue_create("jimbo_queue", DISPATCH_QUEUE_CONCURRENT);

for (int i = 0; i < 20; i++) {
    
    
    dispatch_async(queue, ^{
    
    
       //read
        NSLog(@"read");
        sleep(1);
    });
    dispatch_barrier_async(queue, ^{
    
    
        NSLog(@"writer");
        sleep(1);
    });
    dispatch_async(queue, ^{
    
    
       //read
        NSLog(@"read");
        sleep(1);
    });
}

Atomic、nonatomic

可参考objc -> objc-accessors.mm文件内

//get方法
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    
    
    if (offset == 0) {
    
    
        return object_getClass(self);
    }

    // Retain release world
    id *slot = (id*) ((char*)self + offset);
    if (!atomic) return *slot;
        
    //atomic 添加自旋锁
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    
    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}


//set方法 的最终调用
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    
    
    if (offset == 0) {
    
    
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

	.......
	
    if (!atomic) {
    
    
        oldValue = *slot;
        *slot = newValue;
    } else {
    
    
    //atomic 添加自旋锁
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}
  • atomic 用于保证属性setter、getter的原子性操作,相当于在其内部线程添加了线程同步的锁
  • 但是它不能保证使用属性的过程是线程安全的. (因为只在方法内部加锁了)
@property(atomic, strong) NSMutableArray *mArr;
---

MYPerson *p = [[MYPerson alloc] init];
p.mArr = @[].mutableCopy; //set 方法线程安全

/**
 这段代码不安全
 p.mArr 这个get是安全的
 addObject 是不安全的,因为是在get方法之外了
*/
[p.mArr addObject:@"1"];

备注:部分笔记包含有MJ老师的学习资料。

猜你喜欢

转载自blog.csdn.net/goldWave01/article/details/122094507
今日推荐