C99:记录线程安全的受限指针?
这个问题不是关于restricted的技术用法,而是关于主观用法。尽管我可能会错误地理解技术上的限制是如何运作的,但在这种情况下,你应该随意拷问我,因为我的问题是基于错误的前提。
以下是到目前为止我如何使用受限制的两个示例:
如果我有一个函数接受指向不可变字符序列的指针,我不会说它是受限制的,因为允许其他人通过自己的访问数据在函数执行的同时,例如从另一个并行线程执行指针。数据没有被修改,所以没问题。
但是,如果该函数采用指向它可能修改的可变字符序列的指针,我说它是受限制的,因为在执行期间绝对不应该从任何指针(显然,函数使用的参数除外)访问数据由于数据可能不一致而导致的功能。它还说明了数据被修改的可能性,因此编码器知道不要读取过时的数据,并且他们应该在访问或其他任何操作时使用内存屏障...
我编写的 C 代码不多,所以我很容易犯错误我在这里假设的。这是限制的正确用法吗?在这种情况下值得这样做吗?
我还假设,一旦函数返回时将受限制的指针从堆栈中弹出,则可以再次通过任何其他指针自由访问数据,并且限制仅持续与受限制的指针一样长。我知道这依赖于遵循规则的编码器,因为通过“非官方”指针访问受限数据是 UB。
我做对了吗?
编辑:
我只是想澄清一下,我已经知道它绝对不会阻止用户通过多个线程访问数据,而且我也知道 C89 不知道什么是“线程”甚至是。
但考虑到可以通过引用修改参数的任何上下文,很明显,在函数运行时不得访问它。这对强制线程安全没有任何作用,但它确实清楚地记录了您在函数执行期间通过自己的指针修改数据需要您自担风险。
即使线程完全被排除在外,您仍然可以在我看来正确的场景中进行进一步的优化。
即便如此,还是感谢您迄今为止所有的权威答案。我是对我喜欢的所有答案都投赞成票,还是只对我接受的答案投赞成票?如果不止一个被接受怎么办?抱歉,我是新来的,我现在会更彻底地查看常见问题解答......
This question isn't about the technical usage of restricted, more about the subjective usage. Although I might be mistaken as to how restricted technically works, in which case you should feel free to grill me for basing a question on a false premise.
Here are two examples of how I'm using restricted so far:
If I have a function that takes a pointer to a sequence of immutable chars, I don't say it's restricted, since other people are allowed to access the data via their own pointers at the same time as the function's executing, e.g. from another parallel thread. The data isn't being modified, so no problem.
However, if the function takes a pointer to a sequence of mutable chars that it might modify, I say it's restricted because the data absolutely should not be accessed in anyway from any pointer (bar the argument the function uses, obviously) during the execution of the function due to potentially inconsistent data. It also states the possibility of the data being modified, so the coder knows not to read stale data and that they should use a memory barrier when accessing or whatever...
I don't code much C, so I could easily be wrong about what I'm assuming here. Is this correct usage of restrict? Is it worth doing in this scenario?
I'm also assuming that once the restricted pointer is popped off the stack when the function returns, that the data can then freely be accessed via any other pointer again, and that the restriction only lasts as long as the restricted pointer. I know that this relies on the coder following the rules, since accessing a restricted data via an 'unofficial' pointer is UB.
Have I got all of this right?
EDIT:
I'd just like to make clear that I already know it does absolutely nothing to prevent the users from accessing data via multiple threads, and I also know that C89 has no knowledge of what 'threads' even are.
But given that any context where an argument can be modified via reference, it's clear that it mustn't be accessed as the function is running. This doesn't do anything to enforce thread safety, but it does clearly document that you modify the data through your own pointer during the execution of the function at your own risk.
Even if threading is taken completely out of the equation, you still allow for further optimizations in a scenario where it seems correct to me.
Even so, thanks for all your authoritative answers so far. Do I upvote all the answers that I liked, or just the one that I accept? What if more than one is accepted? Sorry, I'm new here, I'll look through the FAQ more thoroughly now...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
restrict
与线程安全无关。事实上,现有的 C 标准根本没有提及线程主题;从规范的角度来看,不存在“线程”这样的东西。restrict
是一种通知编译器有关别名的方法。指针通常使编译器很难生成有效的代码,因为编译器在编译时无法知道两个指针是否实际上引用同一内存。玩具示例:当编译器处理此函数时,它不知道 x 和 y 是否引用相同的内存位置。因此它不知道是否会打印
5
还是7
,并且它必须在调用之前发出代码以实际读取
*x
printf 。但如果您将
x
声明为int *restrict x
,编译器可以证明该函数打印5
,因此它可以将编译时常量提供给 printf 调用。许多此类优化都可以通过
restrict
实现,特别是当您谈论数组操作时。但这一切都与线程无关。为了正确使用多线程应用程序,您需要适当的同步原语,例如互斥体、条件变量和内存屏障...所有这些都是特定于您的平台的,并且与限制没有任何关系>。
[编辑]
为了回答您关于使用
restrict
作为文档形式的问题,我也对此说“不”。您似乎认为应该记录变量何时可以或不能同时访问。但对于共享数据,正确的原则是(几乎总是)确保它永远不会被并发访问。
变量的相关文档是是否它是否被共享以及哪个互斥体保护它。任何出于任何原因访问该变量的代码,即使只是为了读取它,都需要保存互斥锁。作者既不应该知道也不关心其他线程是否可能会或可能不会同时访问该变量...因为其他线程将遵守相同的规则,并且互斥体保证不存在并发访问。
这个规则很简单,它有效并且可以扩展,这就是为什么它是在线程之间共享数据的主要范例之一。 (另一个是消息传递。)如果您发现自己试图推理“这次我真的需要锁定互斥体吗?”,那么您几乎肯定做错了什么。这一点怎么强调都不为过。
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:When the compiler processes this function, it has no idea whether
x
andy
refer to the same memory location. Therefore it does not know whether it will print5
or7
, and it has to emit code to actually read*x
before callingprintf
.But if you declare
x
asint *restrict x
, the compiler can prove that this function prints5
, so it can feed a compile-time constant to theprintf
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.
不,我不认为这是提供有关不同线程访问的任何信息的好方言。它意味着关于指针的断言,特定的代码和平处理它处理的不同指针。线程还不是该语言的一部分。线程安全地访问数据需要更多,
restrict
不是正确的工具。易失性
并不是一个保证,有时您也会看到建议。即将推出的标准 C1x 应该提供此类构造。 C99 不是。
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.The upcoming standard C1x is supposed to provide such constructs. C99 isn't.
restrict
的典型示例是memcpy()
- OS X 10.6 上的联机帮助页给出的原型为:memcpy
中的源区域和目标区域不允许重叠;因此,通过将它们标记为restrict
来强制执行此限制 - 编译器可以知道源数组的任何部分都不会与目标数组别名;它可以执行诸如读取大量源数据然后将其写入目标数据之类的操作吗?本质上,
restrict
是通过将指针标记为非别名来帮助编译器优化 - 它本身对线程安全没有帮助 - 它不会自动导致函数指向的对象被锁定被称为。请参见如何使用 C 中限定的限制 和 关于限制的维基百科文章进行更长时间的讨论。
The canonical example of
restrict
ismemcpy()
- which the manpage on OS X 10.6 gives the prototype as:The source and destination regions in
memcpy
are not permitted to overlap; therefore by labelling them asrestrict
this restriction is enforced - the compiler can know that no part of the source array aliases with the destination; can it can do things like read a large chunk of the source and then write it into the destination.Essentially
restrict
is about assisting compiler obtimizations by tagging pointers as not aliasing - in and of itself it doeen't help with thread safety - it doesn't automatically cause the object pointed to be to locked while the function is called.See How to use the restrict qualified in C and the wikipedia article on restrict for a more lengthy discussion.
restrict
向编译器提示,通过指针访问的缓冲区不会通过作用域中的另一个指针进行别名。因此,如果您有这样的功能:不使用
restrict
不足以在多线程应用程序中提供受保护的访问。如果您有一个共享缓冲区,由多个线程通过 char * 参数以读/写方式同时访问,您可能需要使用某种锁/互斥体等 - 缺少restrict< /code> 并不意味着线程安全。
希望这有帮助。
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: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 viachar *
parameters in a read/write way you would potentially need to use some kind of lock/mutex etc - the absence ofrestrict
does not imply thread safety.Hope this helps.