5

这个问题不是关于受限的技术使用,更多的是关于主观使用。虽然我可能会误认为限制在技术上是如何工作的,但在这种情况下,您应该随时指责我基于错误前提提出问题。

以下是到目前为止我如何使用受限的两个示例:

如果我有一个函数需要一个指向不可变字符序列的指针,我不会说它受到限制,因为其他人可以在函数执行的同时通过他们自己的指针访问数据,例如从另一个并行线。数据没有被修改,所以没问题。

但是,如果函数接受一个指向它可能修改的可变字符序列的指针,我说它是受限制的,因为在执行由于数据可能不一致而导致的函数。它还说明了数据被修改的可能性,因此编码人员知道不要读取过时的数据,并且他们应该在访问时使用内存屏障或其他...

我没有编写太多 C 代码,所以我很容易对我在这里的假设有所误解。这是限制的正确用法吗?在这种情况下值得做吗?

我还假设一旦函数返回时将受限指针从堆栈中弹出,然后可以再次通过任何其他指针自由访问数据,并且限制只持续与受限指针一样长。我知道这依赖于遵循规则的编码器,因为通过“非官方”指针访问受限数据是 UB。

我做对了这一切吗?

编辑:

我想明确一点,我已经知道它绝对不会阻止用户通过多个线程访问数据,而且我也知道 C89 甚至不知道“线程”是什么。

但是考虑到可以通过引用修改参数的任何上下文,很明显在函数运行时不能访问它。这对强制线程安全没有任何作用,但它确实清楚地记录了您在函数执行期间通过自己的指针修改数据,风险自负

即使线程完全被排除在外,您仍然允许在我认为正确的情况下进行进一步优化。

即便如此,感谢您迄今为止提供的所有权威答案。我是赞成所有我喜欢的答案,还是只赞成我接受的答案?如果接受多于一个怎么办?抱歉,我是新来的,我现在会更彻底地查看常见问题解答...

4

4 回答 4

5

restrict has nothing to do with thread safety. In fact, the existing C standards have nothing to say on the topic of threads at all; from the point of view of the spec, there is no such thing as a "thread".

restrict is a way to inform the compiler about aliasing. Pointers often make it hard for the compiler to generate efficient code, because the compiler cannot know at compile time whether two pointers actually refer to the same memory. Toy example:

void foo(int *x, int *y) {
    *x = 5;
    *y = 7;
    printf("%d\n", *x);
}

When the compiler processes this function, it has no idea whether x and y refer to the same memory location. Therefore it does not know whether it will print 5 or 7, and it has to emit code to actually read *x before calling printf.

But if you declare x as int *restrict x, the compiler can prove that this function prints 5, so it can feed a compile-time constant to the printf call.

Many such optimizations become possible with restrict, especially when you are talking about operations on arrays.

But none of this has anything to do with threads. To get multi-treading applications right, you need proper synchronization primitives like mutexes, condition variables, and memory barriers... All of which are specific to your platform, and none of which have anything to do with restrict.

[edit]

To answer your question about using restrict as a form of documentation, I would say "no" to that, also.

You seem to be thinking that you should document when a variable can or cannot be concurrently accessed. But for shared data, the proper discipline is (almost always) to ensure that it is never concurrently accessed.

The relevant documentation for a variable is whether it is shared at all and which mutex protects it. Any code accessing that variable for any reason, even just to read it, needs to hold the mutex. The author should neither know nor care whether some other thread might or might not happen to be accessing the variable concurrently... Because that other thread will be obeying the same discipline, and the mutex guarantees there is no concurrent access.

This discipline is simple, it works, and it scales, which is why it is one of the dominant paradigms for sharing data between threads. (The other is message passing.) If you ever find yourself trying to reason "do I really need to lock the mutex this time?", you are almost certainly doing something wrong. It would be hard to overstate this point.

于 2011-07-16T16:55:43.253 回答
3

No, I don't think that this is a good dialect to provide any information about acces from different threads. It is meant as assertions about pointers that a particular peace of code gets for different pointers it handles. Threading is not part of the language, yet. Thread safe acces to data needs much more, restrict is not the right tool. volatile isn't a guarantee, which you sometimes see proposed as well.

  • mutexes or other lock structures
  • memory fences that ensure data integrity
  • atomic operations and types

The upcoming standard C1x is supposed to provide such constructs. C99 isn't.

于 2011-07-16T16:55:49.083 回答
1

restrict is a hint to the compiler that the buffer accessed via a pointer is not aliased via another pointer in scope. So if you have a function like:

foo(char *restrict datai, char *restrict dataj)
{
    // we've "promised" the compiler that it should not worry about overlap between 
    // datai and dataj buffers, this gives the compiler the opportunity to generate 
    // "better" code, potentially mitigating load-store issues, amongst other things
}

To not use restrict is not enough to provide guarded access in a multi-threaded application though. If you have a shared buffer simultaneously accessed by multiple threads via char * parameters in a read/write way you would potentially need to use some kind of lock/mutex etc - the absence of restrict does not imply thread safety.

Hope this helps.

于 2011-07-16T16:56:43.423 回答
1

的典型示例restrictmemcpy()- OS X 10.6 上的手册页将原型给出为:

void* memcpy(void *restrict s1, const void *restrict s2, size_t n);

中的源区域和目的区域memcpy不允许重叠;因此,通过将它们标记restrict为强制执行此限制 - 编译器可以知道源数组的任何部分都不会与目标别名;它可以执行诸如读取大量源然后将其写入目标的操作吗?

本质上restrict是关于通过将指针标记为非别名来帮助编译器优化 - 就其本身而言,它对线程安全没有帮助 - 它不会在调用函数时自动导致指向的对象被锁定。

请参阅如何使用 C 中限定的限制关于限制的维基百科文章以进行更冗长的讨论。

于 2011-07-16T16:46:11.480 回答