根据aio_read / write上的文档,AIO库基本上有两种方式可以通知您的应用程序异步文件I / O操作已完成。 1)您可以使用信号,2)您可以使用回调函数
我认为回调函数比信号更受欢迎,并且可能更容易集成到更高级别的多线程库中。不幸的是,至少可以说这个功能的文档很乱。某些源(例如man page for the sigevent struct)表示您需要将sigevent结构中的sigev_notify数据成员设置为SIGEV_CALLBACK,然后提供函数处理程序。据推测,处理程序在同一个线程中调用。其他documentation表示你需要将sigev_notify设置为SIGEV_THREAD,它将在新创建的线程中调用回调处理程序。
无论如何,在我的Linux系统(带有2.6.28内核的Ubuntu)上,SIGEV_CALLBACK似乎没有在任何地方定义,但SIGEV_THREAD的工作方式与宣传的一样。不幸的是,创建一个新线程来调用回调处理程序似乎效率很低,特别是如果你需要调用许多处理程序。最好使用现有的线程池,类似于大多数网络I / O事件多路分解器的工作方式。某些版本的UNIX(如QNX)包含一个SIGEV_SIGNAL_THREAD标志,它允许您使用指定的现有线程调用处理程序,但这似乎在Linux上不可用,它似乎甚至不是POSIX的一部分标准。
那么,是否可以以在预先分配的后台线程/线程池中调用用户处理程序的方式使用POSIX AIO库,而不是每次调用处理程序时创建/销毁新线程?
答案 0 :(得分:2)
我通常发现通过在专用后台线程或线程中执行普通IO来模拟异步IO更简单,更便携,以我喜欢的方式调度完成回调。
答案 1 :(得分:1)
一种方法是使用SIGEV_SIGNAL
和realtime signal将“就绪文件描述符”“携带”到信号处理程序。实时信号队列和信号处理程序在一个线程中异步执行,因此这种方法在功能上或多或少等同于SIGEV_CALLBACK
:
/*
* Warning! Untested!
* Also, safe initialization, per-thread signal masking and
* error-checking omitted.
*/
static void my_callback(int sig, siginfo_t *info, void *context) {
int fd;
fd = info->si_value.sival_int;
/* ...enqueue the fd for processing... */
}
struct sigaction sa;
sa.sa_handler = my_callback; /* Register our async callback */
sa.sa_flags = SA_SIGINFO;
sigaction(SIGRTMIN+1, &sa, NULL);
...
struct aiocb ac;
ac.aio_filedes = some_fd;
ac.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
ac.aio_sigevent.sigev_signo = SIGRTMIN+1; /* Associate callback w. aiocb */
....
aio_read(&ac);
现在你的 my_callback 将在一个线程中异步触发,并由你将fd传递给你的helper线程池。另请参阅this bit of SGI code,了解在SIGEV_SIGNAL
不可用时如何回退到SIGEV_CALLBACK
。
答案 2 :(得分:0)
如果您担心每次完成调用都会创建/销毁多个线程,为什么不批量生成IO?
使用list io apis ..
struct aiocb **myaiocb;
[just an array of aiocb pointers, where each aiocb points to an IO buffer
and the operation to be performed, etc]
....
lio_listio(LIO_NOWAIT, myaiocb, num_ios, &sigevent);
列表IO的优点是只有在列表中的所有IO完成(成功/失败)后才会调用回调处理程序。您可以使用aio_return检查每个IO操作的状态。
答案 3 :(得分:0)
这是一个非常古老的帖子,但在查看同样的问题时,它出现在谷歌的顶端。现在有一个GNU扩展[
,它允许您指定aio应该使用的最大线程数以及这些线程的生命周期。