Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Вынуждение ожидающего потока вернуться в пользовательское пространство

Автор вопроса: CommunityОтветов: 1

Можно ли разбудить поток, ожидающий блокировку фьютекса? Я пытался использовать сигнальный механизм, но, похоже, он не работает. Есть ли другие подходы, которые я мог бы попробовать? Ниже я добавил пример, который может быть похож на то, чего я пытаюсь достичь.

  1. У меня есть поток A, который получает блокировку фьютекса "lockA" следующим образом: - ret = syscall(__NR_futex, &lockA, FUTEX_LOCK_PI, 1, 0, NULL, 0);

  2. У меня есть поток B, который пытается получить блокировку фьютекса "lockA" и блокируется в ядре, поскольку поток A получил блокировку. ret = syscall(__NR_futex, &lockA, FUTEX_LOCK_PI, 1, 0, NULL, 0);

  3. Если поток B получит блокировку A, другой поток, поток C, узнает об этом. Если поток B не получает блокировку, поток C хотел бы, чтобы поток B прекратил ожидание блокировки и сделал что-то другое.

Итак, в основном, на данный момент я пытаюсь выяснить, могу ли я заставить поток C "сигнализировать" потоку B, чтобы он больше не блокировался в ядре. Для этого я установил обработчик сигнала в потоке B следующим образом:

struct sigaction act;

act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_restorer = NULL;
sigaction(SIGSYS, &act, NULL);

...
...

void handler() {
  fprintf(stderr, "Inside the handler, outta the kernel\n");
}

Из потока C я пытаюсь отправить сигнал как: - pthread_kill(tid_of_B, SIGSYS);

Что я делаю не так? Можно ли вообще разбудить поток B? Если да, следует ли использовать другой подход?

[EDIT] Основываясь на комментарии ниже, я попытался проверить возвращаемое значение из pthread_kill и понял, что вызов не возвращается.

  • Теоретически, я думаю, отправка сигнала должна работать. Если вы делаете поток занятым ожиданием или sleep(), а затем отправляете сигнал, вызывается ли ваш обработчик? abligh 16.11.2015
  • @ablight Спасибо за ваш ответ. Вы имеете в виду, что поток B должен спать? 16.11.2015
  • Я бы рекомендовал не вызывать асинхронно-небезопасные функции в обработчике сигналов даже для отладки. Если вам действительно нужно протоколировать вызов вашего обработчика сигналов, write() to STDERR_FILENO. EOF 16.11.2015
  • в общем, вызов syscall() не является хорошей идеей, так как базовые номера вызовов и список параметров могут меняться от одного обновления ОС к другому. Предлагайте всегда вызывать библиотечную функцию. в этом случае я думаю, что вызов mutex_lock() проще, понятнее и намного надежнее. user3629249 16.11.2015
  • Какое значение возвращает вызов pthread_kill()? alk 16.11.2015
  • @uki, похоже, вы не используете фьютексы по назначению (см. futex(7)). В частности, обычно вы делаете системный вызов как часть приобретения фьютекса только в случае, когда получение оспаривается. Поскольку это именно тот случай, когда вы хотите, чтобы поток B избегал блокировки, возможно, это признак того, что вы хотите вообще избежать этого системного вызова. John Bollinger 16.11.2015

clinuxmultithreadingmutexfutex

Ответы

Ответ 1 · Craig Estey

Несколько вещей.

Вы используете FUTEX_LOCK_PI, которого нет на странице руководства. Я только что просмотрел исходный код ядра и документ, и оказалось, что эта версия предназначена только для использования внутри самого ядра. Он используется для реализации "мьютекса PI" в качестве замены ядра spinlock.

Если вы используете futex, вы должны реализовать семантику данных в адресе, на который он указывает.

Вот грубый псевдокод и, возможно/вероятно, неправильный пример:

int mysem = 1;

void
lock(void)
{

    // atomic_dec returns new value
    while (1) {
        if (atomic_dec(&mysem) == 0)
            break;
        futex(&mysem,FUTEX_WAIT,...)
    }
}

void
unlock(void)
{

    // non_atomic_swap returns old value
    if (non_atomic_swap(&mysem,1) != 0)
        futex(&mysem,FUTEX_WAKE,...)
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы