如何将数组从一个线程安全地传递到另一个线程?

时间:2013-09-26 12:08:29

标签: ios objective-c macos thread-safety

我正在处理一些未在主线程上完成的音频处理。我的后台线程重复生成浮点数组,我的主线程将用它来向用户显示内容。这是线程安全还是过于简单?由于这是在OpenGL循环中,我想避免阻塞具有atomic锁定的任一线程。

我对存储格式很灵活。 (C数组,NSArray等,双,CGFloat,NSNumber等)

请注意,每次调用第二个方法时,它都可能有也可能没有要处理的实际新数据。它只是想要最新的东西。

@interface

@property (nonatomic, strong) NSMutableArray *generatedNumbers;
@property (nonatomic, strong) NSArray *passedNumbers;
...

@end

@implementation

//This is called over and over repeatedly and an unpredictable rate
- (void) generateSomeNumbers{
    ...
    [self.generatedNumbers removeAllObjects];
    for (Something x in something){
        ...
        [self.generatedNumbers addObject:someNSNumberOrCGFloat];
    }
}

//This is called from the main thread (opengl CADisplayLink)
- (void) doStuffWithLatestGeneratedNumbers{
    self.passedNumbers = [NSArray arrayWithArray:self.generatedNumbers];
    [self doStuffWithNumbers:self.passedNumbers];
}

或者这个怎​​么样?:

@interface

@property (nonatomic, strong) NSMutableArray *generatedNumbers;
@property (nonatomic, strong) NSArray *passedNumbers;
...

@end

@implementation

//This is called over and over repeatedly and an unpredictable rate
- (void) generateSomeNumbers{
    ...
    [self.generatedNumbers removeAllObjects];
    for (Something x in something){
        ...
        [self.generatedNumbers addObject:someNSNumberOrCGFloat];
    }

    [self copyNumbers:self.generatedNumbers];
}

- (void)copyNumbers:(NSArray *)numbers
{
    @autoreleasepool
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.passedNumbers = [NSArray arrayWithArray:self.generatedNumbers];
                });
    }
}

//This is called from the main thread (opengl CADisplayLink)
- (void) doStuffWithLatestGeneratedNumbers{

    [self doStuffWithNumbers:self.passedNumbers];
}

2 个答案:

答案 0 :(得分:1)

  

由于这是在OpenGL循环中,我想避免阻塞   原子锁的线程

这使事情变得更加艰难。除非交换数组值的操作是原子操作(即单个CPU指令),否则您需要进行某种锁定。

如果您处理的值大于CPU的本机指令大小,则分配替换的操作将不是原子操作。

分配NSMutableArray指针原子,但除非你乐意泄漏它正在替换的数组,否则你需要释放现有的数组。这两个操作的组合不是原子操作,因此需要使用锁保护(这是将属性标记为atomic)。

我建议仔细检查一下你不能锁定。 The lock in question is a spin-lock protecting two instructions,所以即使在竞争时它也会非常便宜。如果您正在使用Objective-C,或者从堆中分配任何内存,那么您可能已经在过高的抽象级别工作以担心这种细节(做任何事情都涉及到锁定)。

要避免使用Objective-C或甚至分配内存来获取锁定的情况的示例是实时自动处理。由于锁定的持续时间不是完全可预测的,并且实时音频线程具有非常紧密的期限(比OpenGL环路更紧密 - 有时小于1ms,任何超限导致可听到的毛刺),它们无法安全使用。然后,在线程之间传递数据的标准方法是circular buffer

答案 1 :(得分:0)

我就是这样做的:

@property (strong) NSArray* generatedNumbers;

....

- (void) generateSomeNumbers{
    NSMutableArray* tempNumbers = [[NSMutableArray alloc] init];

    for (Something x in something){
        ...
        [tempNumbers addObject:someNSNumberOrCGFloat];
    }

    [self setGeneratedNumbers: tempNumbers];
}

//This is called from the main thread (opengl CADisplayLink)
- (void) doStuffWithLatestGeneratedNumbers{

    [self doStuffWithNumbers: [self generatedNumbers]];
}

以上假设generatedNumbers在generateNumbers中赋值后并未发生变异,我们也使用ARC。

请注意,该属性是原子的。我认为这是必要的,以阻止潜在的竞争条件,其中主线程试图获得generatedNumbers但该数组在其有机会进行保留之前被另一个线程释放。