多个线程同时操作同一个socket文件描述符比较麻烦
· 如果一个线程正在阻塞地read(2)某个socket,而另一个线程close(2)了此socket。
· 更糟糕的是,一个线程正准备read(2)某个socket,而另一个线程close(2)了此socket;第三个线程又恰好open(2)了另一个文件描述符,其fd号码正好与前面的socket相同。这样程序的逻辑就混乱了
socket读写的特点是不保证完整性,读100字节有可能只返回20字节,写操作也是一样的。
· 如果两个线程同时write同一个TCP socket,每个线程都只发出去半条消息,那接收方收到数据如何处理?
· 对于非阻塞IO,情况是一样的,而且收发消息的完整性与原子性几乎不可能用锁来保证,因为这样会阻塞其他IO线程。
考虑磁盘IO
应当保证的是
一个线程可以操作多个文件描述符,但一个线程不能操作别的线程拥有的文件描述符。
为了稳妥起见,我们应该把对同一个epoll fd的操作(添加、删除、修改、等待)都放到同一个线程中执行。
对于磁盘文件,在必要的时候多个线程可以同时调用pread(2)/pwrite(2)来读写同一个文件;
在C++里解决这个问题的办法很简单:RAII。用Socket对象包装文件描述符,所有对此文件描述符的读写操作都通过此对象进行,在对象的析构函数里关闭文件描述符。这样一来,只要Socket对象还活着,就不会有其他Socket对象跟它有一样的文件描述符,也就不可能串话。
否则的话,旧的TCP连接A一断开,TcpConnection对象销毁,关闭了旧的文件描述符(RAII),而且新连接B的socket文件描述符有可能等于之前断开的TCP连接(这是完全可能的,POSIX要求每次新建文件描述符时选取当前最小的可用的整数)。当程序处理完旧连接的request时,就有可能把response发给新的TCP连接B,造成串话。
No comments yet.