В моей программе на C++ я использую вызов lio_listio
для отправки многих (до нескольких сотен) запросов на запись сразу. После этого я делаю некоторые вычисления, и когда я закончил, мне нужно дождаться завершения всех выдающихся запросов, прежде чем я могу отправить следующую партию запросов. Как я могу это сделать?lio_listio: Как дождаться завершения всех запросов?
Прямо сейчас, я просто звоню aio_suspend
в цикле, с одним запросом на звонок, но это кажется уродливым. Похоже, я должен использовать аргумент struct sigevent *sevp
для lio_listio
. Мой текущий догадаться, что я должен сделать что-то вроде этого:
- В главном потоке, создать мьютекс и зафиксировать его как раз перед вызовом
lio_listio
. - В вызове
lio_listio
укажите функцию уведомления/обработчик сигнала, который открывает этот мьютекс.
Это должно дать мне желаемое поведение, но будет ли оно надежно работать? Разрешено ли манипулировать мьютексами из контекста обработчика сигнала? Я прочитал, что мьютексы pthread могут обеспечить обнаружение ошибок и потерпеть неудачу, если вы попытаетесь заблокировать их снова из одного потока или разблокировать их из другого потока, но это решение зависит от взаимоблокировки.
Пример кода, с помощью обработчика сигнала:
void notify(int, siginfo_t *info, void *) {
pthread_mutex_unlock((pthread_mutex_t *) info->si_value);
}
void output() {
pthread_mutex_t iomutex = PTHREAD_MUTEX_INITIALIZER;
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_sigaction = ¬ify;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &act, NULL);
for (...) {
pthread_mutex_lock(&iomutex);
// do some calculations here...
struct aiocb *cblist[];
int cbno;
// set up the aio request list - omitted
struct sigevent sev;
memset(&sev, 0, sizeof(struct sigevent));
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
sev.sigev_value.sival_ptr = &iomutex;
lio_listio(LIO_NOWAIT, cblist, cbno, &sev);
}
// ensure that the last queued operation completes
// before this function returns
pthread_mutex_lock(&iomutex);
pthread_mutex_unlock(&iomutex);
}
Пример кода, с помощью функции уведомления - возможно, менее эффективным, так как дополнительный поток создается:
void output() {
pthread_mutex_t iomutex = PTHREAD_MUTEX_INITIALIZER;
for (...) {
pthread_mutex_lock(&iomutex);
// do some calculations here...
struct aiocb *cblist[];
int cbno;
// set up the aio request list - omitted
struct sigevent sev;
memset(&sev, 0, sizeof(struct sigevent));
sev.sigev_notify = SIGEV_THREAD;
sev_sigev_notify_function = &pthread_mutex_unlock;
sev.sigev_value.sival_ptr = &iomutex;
lio_listio(LIO_NOWAIT, cblist, cbno, &sev);
}
// ensure that the last queued operation completes
// before this function returns
pthread_mutex_lock(&iomutex);
pthread_mutex_unlock(&iomutex);
}
Спасибо за очень информативный пост. Синхронизация каждого цикла - это то, что я хочу, поскольку я использую что-то похожее на двойной буфер для хранения вычисленных данных - если нет другой проблемы с решением mutex, это должно быть хорошо для моей цели. Я не знал об этих ограничениях lio_listio() - я буду исследовать, было бы лучше использовать файл mmap. –
Да, в этом случае я бы предположил, что ваше решение с мьютексом отлично работает. Единственное, о чем я бы немного беспокоился, это шаблон освобождения мьютекса внутри обработчика сигнала, возможно, в потоке, который в настоящий момент заблокирован при приобретении блокировки (для списка системных вызовов, защищенных сигналами: http: // pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03). вы используете мьютекс в шаблоне, где традиционно используются семафоры. – Arvid
Действительно, похоже, что функции pthread_mutex_ * не являются безопасными для асинхронного сигнала, а sem_post -. –