1

我正在处理一些不在主线程上完成的音频处理。我的后台线程反复生成浮动数组,我的主线程将使用这些浮动数组向用户显示某些内容。这会是线程安全的还是过于简单化了?由于这是在 OpenGL 循环中,因此我想避免使用atomic锁定阻塞任一线程。

我对存储格式很灵活。(C 数组、NSArray 等和 double、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];
}
4

2 回答 2

1

由于这是在 OpenGL 循环中,我想避免使用原子锁定阻塞任一线程

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

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

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

我建议您仔细检查一下您不能在这里锁定。有问题的锁是一个保护两条指令的自旋锁,因此即使在竞争时它也会非常便宜。如果您使用的是 Objective-C,或者根本就从堆中分配任何内存,那么您可能已经在太高的抽象级别上工作而不必担心这种细节(做任何事情都已经涉及获取锁)。

在实时自动处理中,需要避免使用锁定、使用 Objective-C 甚至分配内存的情况示例。由于锁定的持续时间不是完全可预测的,并且实时音频线程的截止日期非常紧迫(比 OpenGL 循环更紧——有时不到 1 毫秒,任何溢出都会导致可听见的故障),它们不能安全地使用。在线程之间传递数据的标准方式是循环缓冲区

于 2013-09-26T13:01:45.687 回答
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但数组在它有机会进行保留之前被另一个线程释放。

于 2013-09-26T12:34:57.267 回答