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

多线程数据安全

作者: 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;

相关文章

  • iOS 多线程技术有些啥,如何解决多线程带来的隐患

    有多线程就有因为多线程造成的数据安全问题(如何确保同一块共享内存在多线程下不发生数据错乱和数据安全问题) 线程同步...

  • ThreadLocal解析

    原理 产生线程安全问题的根源在于多线程之间的数据共享。如果没有数据共享,就没有多线程并发安全问题。ThreadLo...

  • ThreadLocal解析

    原理 产生线程安全问题的根源在于多线程之间的数据共享。如果没有数据共享,就没有多线程并发安全问题。ThreadLo...

  • java并发关键字 Synchronized关键字

    在多线程环境下为了保证数据安全,需要用到互斥锁保证多线程对数据操作的安全性。Synchronized关键字可以修饰...

  • 线程安全知多少

    1. 如何定义线程安全 线程安全,拆开来看: 线程:指多线程的应用场景下。 安全:指数据安全。 多线程就不用过多介...

  • FMDB 中的多线程处理

    对于数据操作,最重要的一点就是数据安全的问题,在多线程中,线程安全是数据安全的首要前提,下面谈谈FMDB 是如何对...

  • Java学习笔记 23 - 线程之多线程安全、死锁、同步机制

    本文主要内容1、多线程安全问题2、等待唤醒机制 01线程安全 A:线程安全问题引发多线程并发访问同一个数据资源时,...

  • 线程安全之:同步锁

    一、线程安全 线程安全是多线程编程中的一个概念,在多线程的执行过程中访问修改某个共享数据时,线程安全的代码会在同步...

  • 多线程安全性和Java中的锁

    Java是天生的并发语言。多线程在带来更高效率的同时,又带来了数据安全性问题。一般我们将多线程的数据安全性问题分为...

  • Java基础——多线程

    线程安全(更具体的会在多线程当中讲解): 安全 -- 同步 -- 数据是安全 不安全 -- 不同步 -- 效率高 ...

网友评论

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

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