• iOS开发线程安全问题


    先来看一下代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.testStr = @"String initial complete";
        [self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSLog(@"before sleep %@",self.testStr);
            sleep(3);
            NSLog(@"after sleep %@",self.testStr);
        });
    }
    - (void)changeStr{
        
        self.testStr = @"String has changed";
    }

    执行结果:

    2016-10-14 09:11:40.525 线程安全测试[49097:11809626] before sleep String initial complete
    2016-10-14 09:11:43.598 线程安全测试[49097:11809626] after sleep String has changed

    会发现在异步执行中如果testStr改变了,那么异步线程里的testStr也会改变这样就没法保证异步对资源独占操作

    如果在异步block里创建一个str赋值如下代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.testStr = @"String initial complete";
        [self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSString *str = self.testStr;
            NSLog(@"before sleep %@",str);
            sleep(3);
            NSLog(@"after sleep %@",str);
        });
    }
    - (void)changeStr{
        
        self.testStr = @"String has changed";
    }

    执行结果:

    2016-10-14 09:15:09.785 线程安全测试[49319:11822894] before sleep String initial complete
    2016-10-14 09:15:12.786 线程安全测试[49319:11822894] after sleep String initial complete

    这样新的string就不会受到外部改变的影响,但是如果在这个赋值时刻self.asStr已变成野指针那么后面的操作还是会出错,虽然这样情况不是那么容易出现。

    如何防止这种情况呢,可以看看下面的代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.testStr = @"String initial complete";
        [self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
        __weak __typeof(self.testStr) weakString = self.testStr;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            __strong __typeof(weakString) strongString = weakString;
            if(strongString) {
                NSLog(@"before sleep %@",strongString);
                sleep(3);
                NSLog(@"after sleep %@",strongString);
            }
        });
    }
    - (void)changeStr{
        
        self.testStr = @"String has changed";
    }

    执行结果:

    2016-10-14 09:18:40.320 线程安全测试[49541:11835480] before sleep String initial complete
    2016-10-14 09:18:43.388 线程安全测试[49541:11835480] after sleep String initial complete

    weakString会在self.asStr释放时置为nil,如果不是nil时,能够确保对象在block调用的完整周期里面被retain,如果被抢占对strongString的执行会继续并且会产生一样的值,如果strongString执行到时是nil,那么block不能正确执行前已经返回,这样就不会出现先前那样的问题。

    还可以用锁来保证多个线程对一份资源在操作时不会被更改

    #import "ViewController.h"
    #include <pthread.h>
    @interface ViewController ()
    {
        pthread_mutex_t _mutex;
    }
    @property (nonatomic, copy)NSString *testStr;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        pthread_mutex_init(&_mutex, NULL);
        self.testStr = @"String initial complete";
        [self performSelector:@selector(changeStr) withObject:nil afterDelay:0.5];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            pthread_mutex_lock(&_mutex);
            NSLog(@"before sleep %@",self.testStr);
            sleep(3);
            NSLog(@"after sleep %@",self.testStr);
            pthread_mutex_unlock(&_mutex);
        });
    }
    - (void)changeStr{
        
        pthread_mutex_lock(&_mutex);
        self.testStr = @"string has changed";
        pthread_mutex_unlock(&_mutex);
    }
    - (void)dealloc
    {
        pthread_mutex_destroy(&_mutex);
    }
    @end

    执行结果:

    2016-10-14 09:22:57.194 线程安全测试[49824:11850779] before sleep String initial complete
    2016-10-14 09:23:00.269 线程安全测试[49824:11850779] after sleep String initial complete

    在RAC中使用的是OSSpinLock来保证线程安全的,不过几位苹果工程师在swift-dev邮件列表中讨论weak属性的线程安全问题的邮件里爆出自旋锁有bug,邮件地址:https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html。大概就是不同优先级线程调度算法会有优先级反转问题,比如低优先级获锁访问资源,高优先级尝试访问时会等待,这时低优先级又没法争过高优先级导致任务无法完成lock释放不了。也可以看看ReactiveCo社区的讨论https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2619

    本来OSSpinLock是性能最高的锁,但是由于如果不在同一个优先级线程进行锁操作就不能保证安全,那么dispatch_semaphore和pthread_mutex这种仅次于自旋锁的可以作为替代方案。我注意到facebook的KVOController在2016年5月17日时的一个Commit里将所有OSSpinLock替换成了pthread_mutex,可参看这个commithttps://github.com/facebook/KVOController/commit/4f5c329b26f48b151eed82da085288763e2e1761。pthread_mutex会在新系统中性能得到很大的提升,所以可以考虑这个方案。

  • 相关阅读:
    不会写研发部门OKR?来这里看看吧
    HR 必须了解的绩效考核
    成功的OKR复盘会需要注意什么
    effective解读-第一条 静态工厂创建对象代替构造器
    灾难恢复:邮箱数据库操作总结:整理 查询邮箱数据库大小和空白数据大小(重要文档)
    Exchange传输队列queue数据库mail.que文件越来越大(重要文档)
    Exchange2016邮件流(重要文档)
    Exchange2016服务器使用到的9个端口
    VMware EXSI 启用vMotion
    Exchange2016DAG的配置
  • 原文地址:https://www.cnblogs.com/czc-wjm/p/5959104.html
Copyright © 2020-2023  润新知