如果在两个不同的线程中,在同一个文件描述符上调用read
(或write
,或两者都调用),而不明确使用同步机制,会发生什么?
读取和写入是系统调用,因此,在单核CPU上,两次读取"同时"执行可能是不吉利的。但是有多个核心。。。
linux内核将做什么?
让我们更笼统一点:其他内核(比如BSD)的行为总是一样的吗?
编辑:根据关闭文档,我们应该确保文件描述符没有被其他线程中的系统调用使用。因此,它结合了在关闭文件描述符之前需要显式同步(因此,如果可能调用它的线程仍在运行,也可以在读/写前后进行)。
在所有主流的类UNIX操作系统中,任何系统级(syscall)文件描述符访问都是线程安全的。尽管根据年龄的不同,它们不一定是安全的信号。
如果在两个不同任务的文件描述符上调用read
、write
、accept
或类似的方法,那么内核的内部锁定机制将解决争用问题。
对于读取,每个字节可能只读取一次,写入将按任何未定义的顺序进行。
stdio库函数fread
、fwrite
和co在控制结构上默认也有内部锁定,不过通过使用标志可以禁用它。
关于关闭的评论是,在其他线程可能试图使用文件描述符的任何情况下,关闭文件描述符都没有多大意义。因此,尽管就内核而言,它是"安全的",但它可能会导致奇怪的、难以诊断的角落情况。
如果一个线程关闭了一个文件描述符,而另一个线程正试图从中读取,那么第二个线程可能会出现意外的EBADF错误。更糟糕的是,如果第三个线程同时打开一个新文件,可能会重新分配相同的fd,第二个线程可能会意外地从新文件中读取,而不是它预期的文件。。。
照顾那些追随你脚步的人
用互斥信号保护文件描述符是非常正常的。它消除了对内核行为的任何依赖,因此您的消息边界现在是确定的。然后,你不必引用15489行手册页底部的最后一段,它解释了为什么互斥体不是必要的(我夸大了,但你明白我的意思)
它还让任何阅读代码的人都清楚地知道,文件描述符正被多个线程使用。
附加福利
以这种方式使用互斥有一个附带的好处。假设您有来自不同线程的不同消息,其中一些消息比其他消息更重要。您所需要做的就是设置线程优先级以反映其消息的重要性。这样,操作系统将确保您的消息将按重要性顺序发送,而您只需付出最少的努力。
结果将取决于线程在特定时刻的运行计划。
使用多线程可能避免未定义行为的一种方法是假设您正在执行内存操作。例如,更新链接列表或更改变量等。
如果您使用互斥/信号量/锁或其他一些同步机制,它应该可以正常工作。