【问题标题】:Array mutated while being enumerated from different threads数组在从不同线程枚举时发生突变
【发布时间】:2014-08-28 07:36:54
【问题描述】:

我有一个与 iO 上的线程安全相关的相当令人费解的问题。

在一个单例对象中,我有一个包含字典元素的数组。字典元素包含与正在等待的资源(从互联网下载的图像)相关的对象和字符串。 当资源可用时(图像下载完成),我从数组中检索所有等待该特定资源的委托。

问题是数组(可变)可以被非常频繁地修改(来自不同的线程),并且它在被枚举时发生了修改。

我应该如何解决这个问题?我应该创建一个包含键的可变数组的静态字典吗?但是仍然可以枚举给定键的特定数组,同时为它添加另一个值....

这是(非常不安全的)代码:

        - (void)addDelegate:(id<ImageDelegate>)delegate ForFileId:(NSString *)fileId
{
    if (debug) { NSLog(@"[]adding delegate %@ for fileId: %@",delegate,fileId); }
    NSDictionary *d = @{DELEGATE_KEY: delegate,
                        FILE_ID_KEY : fileId};
    [self.delegatesArray addObject:d];
}

- (void)removeDelegate:(id<ImageDelegate>)delegate forImgUrl:(NSString *)imgUrl
{
    NSString *fileId = [Utils formatLink:imgUrl];

    if (debug) { NSLog(@"[]removing delegate %@ for fileId: %@",delegate,fileId); }

    NSDictionary *toRemove;

    for (NSDictionary *crtD in self.delegatesArray) {
        if ([crtD[FILE_ID_KEY] isEqualToString:fileId] && [crtD[DELEGATE_KEY] isEqual:delegate]) {
            toRemove = crtD;
            break;
        }
    }
    [self.delegatesArray removeObject:toRemove];
    if (debug) { NSLog(@"[]removed delegate %@ for fileId: %@",toRemove,fileId); }

}

- (NSArray *)getAllDelegatesForFileId:(NSString *)fileId
{
    NSMutableArray *requiredDelegates = [NSMutableArray new];
    for (NSDictionary *crtD in self.delegatesArray) {
        if ([crtD[FILE_ID_KEY] isEqualToString:fileId]) {
            [requiredDelegates addObject:crtD];
        }
    }
    NSArray *returnedArray = [NSArray arrayWithArray:requiredDelegates];
    if (debug) { NSLog(@"[] found %d delegates for fileId:%@",[returnedArray count],fileId); }
    return returnedArray;
}

【问题讨论】:

    标签: ios arrays multithreading


    【解决方案1】:

    你有几个选择。

    1. 不要使用快速枚举。使用标准的 for 循环来迭代数组。
    2. 同步对数组的访问,使其一次只能由一个线程使用。

    类似这样的:

    @synchronize (self.delegatesArray) {
        // do something with the array
    }
    

    在数组的每个读/写访问中使用上述方法

    控制访问的另一种(更好的)方法是使用 GCD。使用dispatch_syncdispatch_barrier_sync。后者围绕写访问使用,前者围绕读访问。这样做的好处是允许多个读取器,但只允许一个写入器,并且在写入期间不能读取。

    【讨论】:

    • 我在想这样的事情。 (我会尝试 gcd 选项)。这会产生显着的性能影响吗? (我们说的是每秒最多 30-4 次读写)
    • 任何形式的同步都会对性能产生影响。但不如应用程序崩溃。 :) 如果您在正常工作后遇到任何实际性能问题,请担心。
    • 有一个问题......这个数组..包含代表单元格(集合视图或表格视图)如果发生快进 - 后退滚动然后有时我会看到以下内容:一个单元格会闪烁并显示各种直到显示正确的图像。我相信这是因为障碍? ?有什么建议吗?
    猜你喜欢
    • 2015-04-28
    • 1970-01-01
    • 2012-09-21
    • 2012-05-04
    • 1970-01-01
    • 2012-08-25
    • 2012-07-30
    • 2011-10-04
    • 1970-01-01
    相关资源
    最近更新 更多