我们可以在Unices系统中用于异步I / O警报的工具,例如Linux上的epoll,BSD系统上的kqueue和Solaris / dev / poll或I / O端口,所有这些都使用户可以指定要关联的指针用户想要接收I / O警报的文件描述符。
通常,在该指针中,用户指定指向将抽象文件描述符(例如“ Stream”结构或类似内容)的结构的指针,并且每当有新文件描述符被使用时,用户将分配一个新结构。打开。
例如struct stream { int fd; int flags; callback_t on_read_fn; /* ... */ };
现在,我的问题是:如何安全地取消分配用户在多线程环境中分配的此结构?
我问这个,由于epoll / kqueue / etc的性质:通常,您有一个线程从内核“下载”事件向量,该事件向量包含具有一些I / O准备就绪的文件描述符以及与该文件描述符关联的用户指针。
现在,让我们考虑我有2个线程:T1下载这些事件并对其进行处理,例如调用stream->on_read_fn();
等,然后调用T2,T2仅运行用户代码,用户事件和类似的内容。
[如果T2要关闭文件描述符,只需close(stream->fd);
,T1将不再收到该fd的任何I / O警报,因此可以安全地在其中取消分配stream
结构。
但是如果T1线程已经在它现在正在处理的事件向量中下载了非常相同的文件描述符,又还没有处理该文件描述符,那该怎么办?
[如果在T2之前安排了T1,则可以,但是,如果在T1之前安排了T2,则将关闭文件描述符并取消分配stream
结构,因此线程T1在处理该文件描述符时将有一个用户关联的指针,指向一个已经释放的结构!当然,这将严重崩溃。
[我的意思是,如果线程T1下载了该特定文件描述符的某些I / O警报,T2将从不知道,但是T2都无法预测如果T1将下载某些I / O警报或根本不下载!] >
这非常棘手,它使我的头旋转。有什么想法吗?在这种情况下何时可以安全地释放用户指定的指针?
[注意:我的一个朋友建议在调用close(2)
之前,从epoll / kqueue队列中删除文件描述符。没错,这就是我现在要做的,但这不能解决问题,因为T2可以从epoll / kqueue队列中删除文件描述符,但这不能确保该文件的I / O事件描述符尚未从内核“下载”,将由线程T1很快处理。
我们可以在Unices系统中用于异步I / O警报的工具,例如Linux上的epoll,BSD系统上的kqueue以及Solaris / dev / poll或I / O端口,所有这些都允许用户将指针指定为。 ..
我有完全类似的问题,这就是为什么在新的Linux内核建议中,有人(想记住这个名字)建议为FD实现DISABLED状态,因此如果它已被另一个线程释放,则可以跳过处理。] >
个人,我从多线程epool调用移到了FD上的epool()的单个线程,然后将事件调度到多个线程。自己内部的对象被引用计数,然后由垃圾收集器收集。诚实地工作得很好,并且相对于多线程epool解决方案没有明显的降级...
我宁愿避免在2个线程之间共享相同的数据结构。
我在程序中解决了此问题,方法是不释放结构,而是将其标记为“死”并将其添加到列表中,以便以后可以重用。这样,尽管指针可能已经被重用,但它始终保持有效。