0

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

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

    @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
4

2 回答 2

0

可以同时访问一个资源并不意味着一定会发生崩溃。这与“运气”无关。也许您的代码在没有发生崩溃的情况下系统地运行。

此外,“不是线程安全的”并不意味着“会崩溃”。可能发生任何故障。

于 2017-05-24T03:22:25.943 回答
0

对 an 的并发访问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");
        }
    });
}

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

于 2017-05-24T03:30:21.133 回答