zoukankan      html  css  js  c++  java
  • 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会在新系统中性能得到很大的提升,所以可以考虑这个方案。

  • 相关阅读:
    使用某些 DOCTYPE 时会导致 document.body.scrollTop 失效
    VB.NET 笔记1
    知识管理系统Data Solution研发日记之一 场景设计与需求列出
    知识管理系统Data Solution研发日记之五 网页下载,转换,导入
    折腾了这么多年的.NET开发,也只学会了这么几招 软件开发不是生活的全部,但是好的生活全靠它了
    分享制作精良的知识管理系统 博客园博客备份程序 Site Rebuild
    知识管理系统Data Solution研发日记之四 片段式数据解决方案
    知识管理系统Data Solution研发日记之二 应用程序系列
    知识管理系统Data Solution研发日记之七 源代码与解决方案
    知识管理系统Data Solution研发日记之三 文档解决方案
  • 原文地址:https://www.cnblogs.com/czc-wjm/p/5959104.html
Copyright © 2011-2022 走看看