使用全局并发队列从两个不同的线程变换数组时不会崩溃

时间:2017-05-24 02:17:20

标签: ios objective-c concurrency concurrent-queue

在下面的代码中,我们有一个可变数组,它由两个并发队列进行突变。由于并发队列不是线程安全的,因此理想情况下此代码应该崩溃,但这会在没有任何异常或崩溃的情况下执行。

请帮助我理解这种行为。任何帮助将不胜感激: - )

    @interface ViewController ()
    @property(nonatomic, strong) NSMutableArray *arr;
    @end

    @implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.arr = [NSMutableArray new];
}

-(void)viewDidAppear:(BOOL)animated{

        [super viewDidAppear:animated];

        __weak typeof(self) weakSelf = self;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 20000; i++) {
                    [weakSelf.arr addObject:[NSNumber numberWithInt:i]];
                    NSLog(@"Added %@", [weakSelf.arr lastObject]);
                }

            NSLog(@"Final count %ld", [self.arr count]);
        });

        [self performSelector:@selector(removeObjects) withObject:nil afterDelay:0.1];
    }

    -(void)removeObjects{
        __weak typeof(self) weakSelf = self;

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i < 1000; i++) {
                if (weakSelf.arr.count > 1) {
                    [weakSelf.arr removeObjectAtIndex:0];
                }
                NSLog(@"Remove object");
            }
        });
    }

    @end

2 个答案:

答案 0 :(得分:0)

同时访问一个资源并不意味着崩溃得到保证。这与&#34;幸运无关&#34;无关。也许你的代码会在没有崩溃的情况下系统地运行。

此外,&#34;不是线程安全的&#34;并不意味着&#34;会崩溃&#34;。任何故障都可能发生。

答案 1 :(得分:0)

NSMutableArray的并发访问权限明确导致崩溃,但可能导致数据损坏,从而导致您的应用因数据无效而崩溃。

考虑一下这个稍微修改过的代码版本:

-(void)viewDidAppear:(BOOL)animated{

    [super viewDidAppear:animated];

    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        for (int i = 0; i < 20000; i++) {
           NSUInteger randomIndex = arc4random_uniform([weakSelf.arr count]);
            [weakSelf.arr insertObject:[NSNumber numberWithInt:i] atIndex:randomIndex];
            NSLog(@"Added %@", weakSelf.arr[randomIndex]);
        }

        NSLog(@"Final count %ld", [self.arr count]);
    });

    [self performSelector:@selector(removeObjects) withObject:nil afterDelay:0.1];
}

-(void)removeObjects{
    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        for (int i = 0; i < 1000; i++) {
            if (weakSelf.arr.count > 1) {
                 NSUInteger randomIndex = arc4random_uniform([weakSelf.arr count]);
                [weakSelf.arr removeObjectAtIndex:randomIndex];
            }
            NSLog(@"Remove object");
        }
    });
}

每次运行时,最终计数值都会略有不同。

如果您添加@synchronized以使阵列访问线程安全,那么您总是得到最终计数为19000

-(void)viewDidAppear:(BOOL)animated{

    [super viewDidAppear:animated];

    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        for (int i = 0; i < 20000; i++) {
            @synchronized (self.arr) {

           NSUInteger randomIndex = arc4random_uniform([weakSelf.arr count]);
            [weakSelf.arr insertObject:[NSNumber numberWithInt:i] atIndex:randomIndex];
            NSLog(@"Added %@", weakSelf.arr[randomIndex]);
            }
        }

        NSLog(@"Final count %ld", [self.arr count]);
    });

    [self performSelector:@selector(removeObjects) withObject:nil afterDelay:0.1];
}

-(void)removeObjects{
    __weak typeof(self) weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        for (int i = 0; i < 1000; i++) {
            if (weakSelf.arr.count > 1) {
                @synchronized (self.arr) {

                 NSUInteger randomIndex = arc4random_uniform([weakSelf.arr count]);
                [weakSelf.arr removeObjectAtIndex:randomIndex];
                }
            }
            NSLog(@"Remove object");
        }
    });
}

在使用快速枚举时对数组进行变换会导致崩溃,但在这种情况下,变异甚至不必来自另一个线程;简单地在枚举循环中改变数组将导致崩溃。