美文网首页
多线程数据安全

多线程数据安全

作者: LucXion | 来源:发表于2021-08-02 09:55 被阅读0次

    属性关键字 atomic 和 nonatomic

    仅仅能够保证setter、getter方法获取数据的完整性,但不能保证多线程安全

    // nonatomic
    -(void)setName:(NSString *)name
     {
         if (_name != name) {
             [_name release];
             //非原子性的getter可能不完整就出现在这里
             _name = [name retain];
         }
     }
    // atomic
     -(void)setName:(NSString *)name
     {
        @synchronized(self) {
            if (_name != name) {
                [_name release];
                _name = [name retain];
            }
        }
     }
    

    多线程数组安全问题:

    • 问题一:多线程同时对同一个数据进行修改,那么无法保证最后修改的数据是我们期望的,有可能是A线程的修改结果,也可能是B线程的修改结果

       /*
       并发队列 + 栅栏  (无法通过同步执行)
       dispatch_barrier 栅栏
       dispatch_barrier_async 不阻塞主线程
       dispatch_barrier_sync 阻塞主线程
       */
          dispatch_async(queue2, ^{
              sleep(2);
              self.person.name = @"Wang";
              NSLog(@"Wang");
          });
          dispatch_async(queue2, ^{
              sleep(1);
              self.person.name = @"King";
              NSLog(@"King");
          });
          
          dispatch_barrier_async(queue, ^{
              sleep(2);
              self.person.age = @"1";
              NSLog(@"1");
          });
          
          dispatch_barrier_async(queue, ^{
              sleep(1);
              self.person.age = @"2";
              NSLog(@"2");
          });
          
          NSLog(@"先走");
      
    • 问题二:在子线程中读取数据的同时,在另一个子线程中删除数据,读取到的可能就是空

      异步async+并发DISPATCH_QUEUE_CONCURRENT = 不阻塞线程,但是不能防止读到空数据

      异步async+串行DISPATCH_QUEUE_SERIAL = 不阻塞线程,但是不能防止读到空数据

      同步sync+并发DISPATCH_QUEUE_CONCURRENT = 阻塞线程,能防止读到空数据

      异步sync+串行DISPATCH_QUEUE_SERIAL = 阻塞线程,能防止读到空数据

      总结: 异步无法按序执行,同步会阻塞线程,可以 加锁 来解决问题或者参照上面用 栅栏

    iOS 开发中用到的锁

    自旋锁、互斥锁、读写锁、信号量、条件锁

    1. 自旋锁,用于多线程同步数据,会造成线程阻碍,直至显示的释放锁。可以避免进程上下文的调度开支,适合只会阻塞很短时间的场合。

      • OSSpinLock
    // OSSpinLock iOS 10 已废弃
    #import <libkern/OSAtomic.h>
    // OS_SPINLOCK_INIT: 默认值为 0,在 locked 状态时就会大于 0,unlocked 状态下为 0
    // OSSpinLockLock(&oslock):上锁,参数为 OSSpinLock 地址
    // OSSpinLockUnlock(&oslock):解锁,参数为 OSSpinLock 地址
    // OSSpinLockTry(&oslock):尝试加锁,可以加锁则 立即加锁 并返回 YES,反之返回 NO
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    (子线程中) {
        OSSpinLockLock(&oslock);
        // doSomething
        OSSpinLockUnlock(&oslock);  
    }
    
    1. 互斥锁,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。(临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。)

        queue = dispatch_queue_create("CONCURRENT122", DISPATCH_QUEUE_CONCURRENT);
        queue2 = dispatch_queue_create("CONCURRENT233", DISPATCH_QUEUE_CONCURRENT);
        queue3 = dispatch_queue_create("CONCURRENddd3", DISPATCH_QUEUE_CONCURRENT);
        
        _lock = [[NSLock alloc]init];
        _lock.name = @"1111";
        
        dispatch_async(queue, ^{
            NSLog(@"in1");
            [self->_lock lock];
            sleep(3);
            NSLog(@"go1");
            [self->_lock unlock];
        });
        sleep(1);
        dispatch_async(queue2, ^{
            NSLog(@"in2");
            [self->_lock lock];
            NSLog(@"go2");
            [self->_lock unlock];
        });
    
    pthread_mutex_t _lock;
    pthread_mutex_init(&_lock, NULL);
    pthread_mutex_lock(&_lock);
    pthread_mutex_unlock(&_lock);
    pthread_mutex_trylock(&_lock)
    pthread_mutex_unlock(&_lock);
    
    • @synchronized
     - (BOOL)isNetworkActivityOccurring {
         @synchronized(self) {
             return self.activityCount > 0;
         }
     }
    
    1. 读写锁,又称共享-互斥锁,
    //加读锁
    pthread_rwlock_rdlock(&rwlock);
    //解锁
    pthread_rwlock_unlock(&rwlock);
    //加写锁
    pthread_rwlock_wrlock(&rwlock);
    //解锁
    pthread_rwlock_unlock(&rwlock);
    
    1. 递归锁:同一个线程可以加锁N次而不会引发死锁。

      • NSRecursiveLock
    - (NSInteger)try:(NSInteger)count {
        // 递归函数内,需要用到递归锁,否则死锁
        [lock lock];
        NSLog(@"输入:%ld",(long)count);
        if (count > 1) {
            count -= 1;
            return [self try:count];
        }else {
            return count;
        }
        [lock unlock];
    }
    
    • pthread_mutex(recursive)
     pthread_mutex_t lock;
     pthread_mutexattr_t attr;
     pthread_mutexattr_init(&attr);
     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
     pthread_mutex_init(&lock, &attr);
     pthread_mutexattr_destroy(&attr);
     pthread_mutex_lock(&lock);
     pthread_mutex_unlock(&lock);
    
    1. 条件锁

      • NSCondition

        遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞掉线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。

    @interface NSCondition : NSObject <NSLocking> {
    @private
        void *_priv;
    }
    - (void)wait;
    - (BOOL)waitUntilDate:(NSDate *)limit;
    - (void)signal;
    - (void)broadcast;
    
    • NSConditionLock
     @interface NSConditionLock : NSObject <NSLocking> {
     @private
         void *_priv;
     }
     - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
     @property (readonly) NSInteger condition;
     - (void)lockWhenCondition:(NSInteger)condition;
     - (BOOL)tryLock;
     - (BOOL)tryLockWhenCondition:(NSInteger)condition;
     - (void)unlockWithCondition:(NSInteger)condition;
     - (BOOL)lockBeforeDate:(NSDate *)limit;
     - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
    

    相关文章

      网友评论

          本文标题:多线程数据安全

          本文链接:https://www.haomeiwen.com/subject/npecvltx.html