C 读取和线程安全 (linux)
Posted
技术标签:
【中文标题】C 读取和线程安全 (linux)【英文标题】:C read and thread safety (linux) 【发布时间】:2013-07-02 17:16:20 【问题描述】:如果你在两个不同的线程中调用read
(或write
,或两者)会发生什么,在同一个文件描述符上(假设我们对本地文件感兴趣,它是一个套接字文件描述符) , 没有显式使用同步机制?
Read 和 Write 是系统调用,因此,在单核 CPU 上,“同时”执行两次读取可能是不幸的。但是多核...
linux内核会做什么?
让我们更笼统一点:其他内核(如 BSD)的行为是否总是相同的?
编辑:根据close documentation,我们应该确保文件描述符没有被其他线程中的系统调用使用。所以它接缝在关闭文件描述符之前需要显式同步(因此,如果可能调用它的线程仍在运行,也围绕读/写)。
【问题讨论】:
你不能不使用同步机制;内核已经为你做到了。 我的意思是,明确使用。所以内核自己完成整个工作,对它们的读/写/调用都不需要任何显式同步? 据我所知,是的。我实际上不是 linux 内核专家,但我无法想象它不是这样。也许会有更具体的知识的人来。 我不知道会发生什么。我永远不会尝试它 - 我有线程打开自己的 fd。 试试看 :),告诉我们 【参考方案1】:在所有主流的类 UNIX 操作系统中,任何系统级(系统调用)文件描述符访问都是线程安全的。 虽然取决于年龄,但它们不一定是信号安全的。
如果您在两个不同任务的文件描述符上调用read
、write
、accept
或类似名称,则内核的内部锁定机制将解决争用。
对于读取,每个字节可能只读取一次,写入将按任何未定义的顺序进行。
stdio 库函数 fread
、fwrite
和 co。默认情况下,控制结构上也有内部锁定,尽管通过使用标志可以禁用它。
【讨论】:
你好。您能否发布将这些函数标记为线程安全的官方文档。 如果您可以为 read/write/accept 和 fread/fwrite 添加一些官方文档的链接,我将非常高兴:) 我很确定 glibc 在 fread 和 fwrite 上不会是线程安全的,除非你与 libpthread 链接。但是当然,如果您使用的是线程,那么这应该已经是真的了。 +1。读/写绝对可以在同一个 fd 上一次调用两次。实际上,当系统调用仍在使用 fd 时关闭()是可能的,但这通常是一个坏主意。人们似乎必须这样做的唯一原因是通过从另一个线程关闭它来打破 fd 上的阻塞系统调用,但我应该指出这种行为严格来说是不可移植的。 (我需要准确查找适用于哪些系统。)因此,文档反对它是正确的:尽管内核会确保您不会关闭系统,但您的应用程序不会快乐。 请注意,如果您在一个线程中调用 write 而另一个线程正在修改写入时传递给内核的缓冲区,则实际写入的数据可能包含也可能不包含一些正在放入的修改数据在另一个线程那里。【参考方案2】:关于关闭的评论是因为在其他线程可能试图使用它的任何情况下关闭文件描述符没有多大意义。因此,虽然就内核而言它是“安全的”,但它可能会导致奇怪的、难以诊断的极端情况。
如果一个线程在第二个线程尝试读取文件描述符时关闭文件描述符,则第二个线程可能会收到意外的 EBADF 错误。更糟糕的是,如果第三个线程同时打开一个新文件,这可能会重新分配相同的 fd,并且第二个线程可能会意外地从新文件中读取,而不是从它预期的文件中读取......
【讨论】:
【参考方案3】:关心那些追随你脚步的人
使用互斥信号量保护文件描述符是完全正常的。它消除了对内核行为的任何依赖,因此您的消息边界现在是确定的。然后,您不必引用 15,489 行手册页底部的最后一段,它解释了为什么不需要互斥锁(我夸大了,但你明白我的意思)
这也让任何阅读你的代码的人都清楚,文件描述符被多个线程使用。
附加福利
以这种方式使用互斥锁有一个附带好处。假设您有来自不同线程的不同消息,并且其中一些消息比其他消息更重要。您需要做的就是设置线程优先级以反映其消息的重要性。这样,操作系统将确保您的消息按重要性顺序发送,而您只需付出最少的努力。
【讨论】:
【参考方案4】:结果将取决于线程如何安排在该特定时刻运行。
使用多线程可能避免未定义行为的一种方法是假设您正在执行内存操作。例如。更新链表或更改变量等。
如果您使用 mutex/semaphores/lock 或其他一些同步机制,它应该可以按预期工作。
【讨论】:
以上是关于C 读取和线程安全 (linux)的主要内容,如果未能解决你的问题,请参考以下文章
C ++如果一个线程写入一旦完成就会切换一个布尔值,那么在另一个线程的循环中读取该布尔值是不是安全?