gpt4 book ai didi

ios - 在Objective-C中需要声明哪些场景关键字 “volatile”?

转载 作者:行者123 更新时间:2023-12-02 03:40:51 25 4
gpt4 key购买 nike

据我所知,volatile通常用于防止某些硬件操作期间意外的编译优化。但是应该在属性定义中声明哪些场景volatile,这让我感到困惑。请举一些有代表性的例子。

谢谢。

最佳答案

编译器假定变量可以更改其值的唯一方法是通过更改变量的代码。

int a = 24;

现在,编译器假定 a24,直到看到任何更改 a值的语句为止。如果您在上面的语句下面的某处写代码,说
int b = a + 3;

编译器会说:“我知道 a是什么,它是 24!所以 b27。我不必编写代码来执行该计算,我知道它将 始终将设为 27”。编译器可能只是优化了整个计算。

但是,如果 a在赋值和计算之间发生了变化,则编译器将是错误的。但是,为什么 a会这样做呢?为什么 a突然具有不同的值?不会的

如果 a是一个堆栈变量,则除非您传递对该变量的引用,否则它不能更改值。
doSomething(&a);

函数 doSomething具有指向 a的指针,这意味着它可以更改 a的值,并且在该行代码之后, a可能不再是 24。所以如果你写
int a = 24;
doSomething(&a);
int b = a + 3;

编译器 不会优化计算。谁知道 a之后 doSomething将具有什么值?编译器肯定不会。

使用对象的全局变量或实例变量,事情变得更加棘手。这些变量不在堆栈上,而是在堆上,这意味着不同的线程可以访问它们。
// Global Scope
int a = 0;

void function ( ) {
a = 24;
b = a + 3;
}
b27吗?答案很可能是肯定的,但是其他线程在这两行代码之间更改了 a的值的可能性很小,那么它就不会是 27。编译器在乎吗?没有为什么?因为C对线程一无所知-至少它不习惯(最新的C标准最终知道 native 线程,但是在那之前的所有线程功能仅是操作系统提供的API,而不是C的 native )。因此,C编译器仍将 b假定为 27并优化计算,这可能会导致错误的结果。

这就是 volatile的优点。如果您像这样标记变量volatile
volatile int a = 0;

您基本上是在告诉编译器:“ a的值可能随时发生变化。不严重,它可能会突然变成蓝色。您不会看到它来了,并且* bang *,它具有不同的值!”。对于编译器而言,这意味着不能仅仅因为 a曾经在1皮秒前具有该值而且没有代码似乎对其进行更改而假定 a具有某个值。没关系访问 pthread_mutex_lock时, 始终读取其当前值。

过度使用volatile会阻止很多编译器优化,可能会大大减慢计算代码的速度,并且在人们甚至不需要使用volatile的情况下,人们经常会使用volatile。例如,编译器永远不会跨内存壁垒进行值(value)假设。内存障碍到底是什么?好吧,这远远超出了我的答复范围。您只需要知道典型的同步结构就是内存障碍,例如锁,互斥或信号灯等。请考虑以下代码:
// Global Scope
int a = 0;

void function ( ) {
a = 24;
pthread_mutex_lock(m);
b = a + 3;
pthread_mutex_unlock(m);
}
pthread_mutex_unlock是一个内存屏障(顺便说一下,也是 a),因此没有必要将 volatile声明为 a,编译器将不会假设整个内存屏障中 atomic的值, 从不

在所有这些方面,Objective-C几乎都类似于C,毕竟它只是具有扩展和运行时的C。要注意的一件事是,Obj-C中的 volatile属性是内存障碍,因此您无需声明属性 atomic。如果您从多个线程访问该属性,则将其声明为 nonatomic,这是默认的方式(如果您未将其标记为 atomic,它将为 nonatomic)。如果您从不从多线程访问它,则将其标记为 volatile可以使对该属性的访问速度大大提高,但这只有在您真正访问该属性很多时才会奏效(很多并不意味着每分钟十次,这相当每秒千次)。

因此,您需要Obj-C代码,需要 volatile 吗?
@implementation SomeObject {
volatile bool done;
}

- (void)someMethod {
done = false;

// Start some background task that performes an action
// and when it is done with that action, it sets `done` to true.
// ...

// Wait till the background task is done
while (!done) {
// Run the runloop for 10 ms, then check again
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]
];
}
}
@end

没有 done,编译器可能会愚蠢地假设 !done在这里永远不会改变,只用 true替换 while (true)即可。 clang是一个永无止境的循环,永远不会终止。

我还没有使用现代编译器对其进行测试。也许当前的 done版本比这更智能。它还可能取决于您如何启动后台任务。如果您分派(dispatch)一个块,则编译器实际上可以轻松查看它是否更改了 done。如果在某处传递对 done的引用,则编译器会知道接收方可能会使用 volatile的值,并且不会做任何假设。但是我很早以前就测试过该代码,当时苹果仍在使用GCC 2.x,而未使用 <libkern/OSAtomic.h>确实导致了一个永无休止的循环,永远不会终止(但仅在启用了优化的发行版本中,而在调试版本中却没有)。因此,我不会依靠编译器足够聪明来正确地完成它。

关于内存屏障的一些有趣的事实:

如果您曾经看过Apple在 x中提供的原子操作,那么您可能想知道为什么每个操作都存在两次:一次为 xBarrier,一次为 OSAtomicAdd32(例如 OSAtomicAdd32Barrier(2))。好了,现在您终于知道了。名称中带有“屏障”的一个是内存屏障,另一个不是。

内存壁垒不仅适用于编译器,还适用于CPU(存在CPU指令,被视为内存壁垒,而普通指令则不被视为内存壁垒)。 CPU需要了解这些障碍,因为CPU喜欢对指令重新排序以无序地执行操作。例如。如果你这样做
a = x + 3 // (1)
b = y * 5 // (2)
c = a + b // (3)

并且加法的流水线很忙,但乘法的流水线并不繁忙,CPU可以先执行 (1)指令,然后再执行 (3),最后所有顺序都无关紧要。这样可以防止管道停顿。同样,CPU很聪明,知道它不能在 (1)(2)之前执行 (3),因为 (2)的结果取决于其他两个计算的结果。

但是,某些种类的顺序更改会破坏代码或程序员的意图。考虑以下示例:
x = y + z // (1)
a = 1 // (2)

加法管道可能很忙,所以为什么不先在 (1)之前执行 a呢?他们彼此不依赖,顺序不重要,对不对?这得看情况。考虑另一个线程监视 a的更改,并且一旦 1变为 x,它将读取 y+z的值,如果按顺序执行了指令,则该值现在应该为 x。但是,如果CPU对它们进行了重新排序,则 (2)将具有它在获得该代码之前所拥有的任何值,并且这有所作为,因为另一个线程现在将以不同的值工作,而不是程序员期望的值。

因此,在这种情况下,顺序将很重要,这就是CPU也需要屏障的原因:CPU不会跨这些屏障对指令进行排序,因此指令 (1)将需要成为屏障指令(或者 (2)NSOperation;取决于CPU)。但是,重新排序指令仅由现代CPU执行,更老的问题是延迟的内存写入。如果CPU延迟了内存写入(在某些CPU中非常常见,因为对于CPU来说内存访问速度非常慢),它将确保在经过内存屏障之前,所有延迟的写入均已执行且已完成,因此所有内存都位于内存中。如果另一个线程现在可以访问它(现在您也知道“内存屏障”的名称实际上来自何处),则返回正确的状态。

您在处理内存障碍方面的工作可能比您甚至还不了解的多(GCD-Grand Central Dispatch充满了这些以及GCD上的 NSOperationQueue/ volatile基础),这就是为什么您真正需要仅在非常罕见的异常(exception)情况下使用 volatile的原因。您可能无需编写100个应用程序,甚至不必使用一次。但是,如果编写许多旨在实现最大性能的低级多线程代码,则迟早会遇到只有 volatile可以授予您正确行为的情况;在这种情况下不使用它会导致奇怪的错误,在这些错误中,循环似乎没有终止,或者变量似乎只是具有错误的值,而您对此没有任何解释。如果遇到此类错误,特别是如果仅在发行版本中看到它们,则可能会在代码中的某个地方错过ojit_code或内存障碍。

关于ios - 在Objective-C中需要声明哪些场景关键字 “volatile”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31535840/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com