【问题标题】:Thread safety: NSOperationQueue + [array addObject]线程安全:NSOperationQueue + [array addObject]
【发布时间】:2011-12-21 23:45:40
【问题描述】:

当使用操作队列时,我找不到任何如何处理相同(类)变量的示例。在 C & 线程中,它是关于互斥锁的。那么,当NSOperationQueue 启动一个线程进行操作并且类变量被修改时会发生什么?它是线程安全的吗?谢谢。

@interface MyTest {
    NSMutableArray *_array;
}
@end

-(id)init
{
    ...
    _array = [NSMutableArray new]; // class variable

        // queue time consuming loading
    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation =
        [NSInvocationOperation initWithTarget:self
                                     selector:@selector(populate)
                                       object:nil];
    [queue addOperation:operation];

        // start continuous processing
    [NSTimer scheduledTimerWithTimeInterval:0.1
                                     target:self
                                   selector:@selector(processing)
                                   userInfo:nil
                                    repeats:YES];
    ...
}

-(void)populate
{
    while (...)
    {
        id element = ...; // time consuming

            // modify class variable "_array" from operation's thread (?)
        [_array addObject:element];

            // Ok, I can do instead of addObject
            // performSelectorOnMainThread:withObject:waitUntilDone:
            // but is it the only way? Is it needed?
    }
}

    // access and/or modify class variable "_array"
-(void)processing
{
    NSLog(@"array.count = %d", array.count);
    for (id i in _array)
    {
        [_array addObject:[NSNumber numberWithInt:rand() % 100]];
            // etc...
    }
}

【问题讨论】:

    标签: iphone ios concurrency nsoperationqueue


    【解决方案1】:

    不,这不是线程安全的,如果您启动一个线程,该线程在一个可以被其他线程修改的类变量上执行某些工作,那么它不是线程安全的,如果在运行填充时从某个线程调用处理另一个,那么当 foreach 循环看到数组已被修改时,您可能会得到一个异常,尽管在您的示例中修改 foreach 循环内的数组时,无论如何您都会得到该异常(您不应该这样做,程序会抛出一个例外)......解决这个问题的一种方法可以是在数组上使用同步块,它将确保同步块不会同时执行,线程阻塞直到一个同步块完成,例如

        -(void)populate
        {
    
    
            while (...)
            {
                id element = ...; // time consuming
    
                    // modify class variable "_array" from operation's thread (?)
          @synchronized(_array)
            {
                [_array addObject:element];
    
            }          // Ok, I can do instead of addObject
                    // performSelectorOnMainThread:withObject:waitUntilDone:
                    // but is it the only way? Is it needed?
            }
    
        }
    
            // access and/or modify class variable "_array"
        -(void)processing
        {
    
    
              @synchronized(_array)
             {
                NSLog(@"array.count = %d", array.count);
                for (id i in _array)
                {
                    //you shouldnt modify the _array here you will get an exception
                        // etc...
                }
            }
        }
    

    【讨论】:

    • 好的,这很清楚。为什么这不是在 Apple 网站上的 ConcurrencyProgrammingGuide (关于操作/队列的那个)中写的?我的意思是,这是我第一次看到这个@synchronized。我现在看到,它在关于线程的部分中进行了描述。
    • 您认为更好的做法是:使用@synchronized 或致电performSelectorOnMainThread
    • 还有一件事:是否可以将_array 声明/访问为atomic 属性?
    • 如果操作需要很长时间,那么调用 performOnMainThread 将阻塞主线程,所以我不会那样做......而且 atomic 对线程安全没有任何保证,只有访问属性,一旦你有你可以做非线程安全的东西的参考,看看这个关于stackoverflow.com/questions/588866/…的答案@
    • 尽管 Apple 甚至指出在 for 循环上运行同步块并没有真正的优势。一般来说,如果您希望在 for 循环中咀嚼某些数据,请将局部变量传递到您的块中,并且仅在操作队列为空后才对全局共享数据进行更改。 [queue operationCount] < 1
    猜你喜欢
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多