假设我们有NSString *req = @"is";
NSMutable *mutableArray = [@[@"this",@"is",@"an",@"array"]mutableCopy];
NSArray *tempArray = [mutableArray copy];
if([tempArray containsObject:req]){
// req is contained in the array
[mutableArray removeObject:req]; //remove the object
}
else{
// req is not contained in the array
}
// update the tempArray if needed
维护一组Container
值,并且每个值都有一个标志,指示该值是否有效。无效值被视为int
。最初,所有值都无效。第一次访问某个值时,它将设置为INT_MAX
并且其标志设置为有效。
INT_MAX
现在,另一个线程同时读取容器值:
struct Container {
int& operator[](int i) {
if (!isValid[i]) {
values[i] = INT_MAX; // (*)
isValid[i] = true; // (**)
}
return values[i];
}
std::vector<int> values;
std::vector<bool> isValid;
};
这是完全有效的代码,但以给定顺序执行行// This member is allowed to overestimate value i, but it must not underestimate it.
int Container::get(int i) {
return isValid[i] ? values[i] : INT_MAX;
}
和(*)
至关重要。
(**)
并且不想使用-O3
。答案 0 :(得分:5)
这里没有同步。如果从一个线程访问这些值并从另一个线程更改它们,则会得到未定义的行为。你需要锁定所有访问,在这种情况下一切都很好。否则,您需要制作所有std::vector
元素atomic<T>
,并且可以使用适当的可见性参数控制值的可见性。
似乎存在对同步和特定原子操作的误解:它们的目的是使代码快速运行!这可能看起来反直觉所以这里是解释:非原子操作应该尽可能快,并且故意无法保证它们如何准确地访问内存。只要编译器和执行系统产生正确的结果,编译器和系统就可以自由地做他们需要或想做的事情。为了实现良好的性能,假设不存在不同线程之间的交互。
然而,在并发系统中,线程之间存在交互。这是原子操作进入阶段的地方:它们允许指定所需的完全必要的同步。因此,它们允许告诉编译器必须遵守的最小约束,以使线程无法正确。编译器将使用这些指示符生成最佳代码以实现指定的内容。该代码可能与不使用任何同步的代码相同,但在实践中通常还需要防止CPU重新排序操作。因此,正确使用同步会产生最有效的代码,只有绝对必要的开销。
棘手的部分是在某种程度上找到需要哪些同步并最小化这些同步。简单地说,没有任何东西将允许编译器和CPU自由地重新排序操作,并且不起作用。
由于提到volatile
的问题,请注意volatile
完全与并发无关! volatile
的主要目的是通知系统地址指向其访问可能有副作用的内存。它主要用于访问内存映射I / O或硬件控制。死于潜在的副作用,它是定义程序语义的C ++的两个方面之一(另一个是使用标准库I / O工具的I / O)。