Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 从不同线程访问只读对象的思考_Objective C_Multithreading_Thread Safety_Nsformatter - Fatal编程技术网

Objective c 从不同线程访问只读对象的思考

Objective c 从不同线程访问只读对象的思考,objective-c,multithreading,thread-safety,nsformatter,Objective C,Multithreading,Thread Safety,Nsformatter,基于我之前在SO(请参阅)中的讨论,这里我要问一个关于对象的更理论性的问题,这些对象在应用程序生命周期中只创建一次(并且从不修改,因此是只读的),并且可以从不同的线程访问。一个简单的用例是核心数据用例。格式化程序可用于不同的线程(主线程、导入线程等) 例如,NSFormatter的创建成本极高。基于此,它们可以创建一次,然后重新使用。下面是一个可以遵循的典型模式(本文中@mattt也强调了这一点) + (NSNumberFormatter *)numberFormatter { stat

基于我之前在SO(请参阅)中的讨论,这里我要问一个关于对象的更理论性的问题,这些对象在应用程序生命周期中只创建一次(并且从不修改,因此是只读的),并且可以从不同的线程访问。一个简单的用例是核心数据用例。格式化程序可用于不同的线程(主线程、导入线程等)

例如,NSFormatter的创建成本极高。基于此,它们可以创建一次,然后重新使用。下面是一个可以遵循的典型模式(本文中@mattt也强调了这一点)

+ (NSNumberFormatter *)numberFormatter {
    static NSNumberFormatter *_numberFormatter = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _numberFormatter = [[NSNumberFormatter alloc] init];
        [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    });

    return _numberFormatter;
}
即使我确信这是一种非常好的方法(创建了一种只读/不可变对象),格式化程序也不是线程安全的,因此以线程安全的方式使用它们可能是危险的。我发现了一个关于这个论点的讨论,作者注意到可能发生崩溃

NSDateFormatter不是线程安全的;有一条背景线 试图同时使用相同的格式化程序(因此 随机性)


那么,从不同线程访问格式化程序时会出现什么问题?是否要遵循安全模式?

格式化程序的具体答案:

在iOS 7/OSX 10.9之前,即使是对格式化程序的只读访问也不是线程安全的。ICU在响应请求时有大量的惰性计算,如果同时进行,可能会崩溃或产生错误的结果

在iOS 7/OSX 10.9中,
NSDateFormatter
NSNumberFormatter
在内部使用锁来序列化对底层ICU代码的访问,从而防止出现此问题

一般答覆:

真正的只读访问/不可变对象实际上通常是线程安全的,但很难判断哪些对象实际上是内部不可变的,哪些对象只是向外部世界呈现一个不可变的接口

在您自己的代码中,您可以知道这一点。当使用其他人的类时,您必须依赖于他们记录的关于如何安全使用他们的类的内容

(编辑,因为请求了对格式化程序进行序列化访问的示例)

我要问的是一个更为理论化的问题,即在应用程序生命周期中只创建一次对象(并且从不修改,因此是只读的)

这在技术上是不正确的:为了使对象真正只读,它也必须是不可变的。例如,可以创建
NSMutableArray
一次,但由于对象允许在创建后进行更改,因此不能将其视为只读,因此在没有额外同步的情况下并发使用是不安全的

此外,逻辑上不可变的对象可能有变异的实现,使它们成为非线程安全的。例如,第一次使用时执行延迟初始化的对象,或者在其实例变量中缓存状态而不进行同步的对象,都不是线程安全的。似乎
NSDateFormatter
就是这样一个类:即使您没有调用方法来改变类的状态,也可能会发生崩溃


解决这个问题的一个方法是:与其为每个应用程序创建一个
NSDateFormatter
,不如为每个线程创建一个,但这也可以让您节省一些CPU周期:提到他们通过使用这个简单的技巧成功地将启动时间缩短了5..10%。

很可能格式化程序在执行其工作时有一些状态被修改,因此对它的多个并发调用最终会破坏该状态。安全模式是为每个线程创建一个格式化程序,或者使用某种互斥来防止多个线程同时访问特定的格式化程序。仅仅因为没有显式修改格式化程序,并不意味着它没有通过使用修改的内部状态。这就是说,我很确定我做得很顺利;可能取决于您的具体使用情况。如果您希望格式化程序上存在大量争用,那么TLS解决方案是可靠的。考虑到格式化与创建格式化程序的相对速度,序列化通常会更有效。谢谢您的回复。非常有用!(在创建格式化程序的调度队列中)调度队列格式化程序队列=调度队列创建(“日期格式化程序队列”,0);(在使用格式化程序的地方)dispatch_sync(formatterQueue,^{(使用格式化程序执行任何操作)});这是有关iOS/OSX版本中不同行为的有趣信息。这是否有文件记录?10.9的基金会发行说明讨论它。我已经提交了一个文档雷达来更新文档,但到目前为止还没有完成。非常感谢!!非常好的讨论。干杯
// in the dispatch_once where you create the formatter
dispatch_queue_t formatterQueue = dispatch_queue_create("date formatter queue", 0);

// where you use the formatter
dispatch_sync(formatterQueue, ^{ (do whatever you wanted to do with the formatter) });