Проснись поток без поддержки ядра - PullRequest
1 голос
/ 30 августа 2011

Есть ли какой-нибудь механизм, с помощью которого я могу разбудить поток в другом процессе, не проходя через ядро?Ожидающий поток может вращаться в цикле, без проблем (каждый поток привязан к отдельному ядру), но в моем случае отправляющий поток должен быть быстрым и не может позволить себе пройти через ядро, чтобы разбудить ожидающий поток.

Ответы [ 3 ]

4 голосов
/ 30 августа 2011

Нет, если другой поток спит (не на процессоре).Чтобы разбудить такой поток, вам нужно изменить его состояние на «RUNNING», вызвав планировщик, который является частью ядра.

Да, вы можете синхронизировать два потока или процессы, если оба работают на разных процессорах, и еслимежду ними есть общая память.Вы должны привязать все потоки к разным процессорам.Затем вы можете использовать спинлок: pthread_spin_lock и pthread_spin_unlock функции из необязательной части Pthread POSIX ('(ADVANCED REALTIME THREADS)'; [THR SPI]);или любой из пользовательских спинлок.Пользовательская спин-блокировка, скорее всего, будет использовать некоторые атомарные операции и / или барьеры памяти.

Отправляющий поток изменяет значение в памяти, которое проверяется в цикле потоком получателя.

Например

init:

pthread_spinlock_t lock;
pthread_spin_lock(&lock);  // close the "mutex"

, затем запускать потоки.

ожидающий поток:

{
pthread_spin_lock(&lock); // wait for event;
work();
}

основной поток:

{
do_smth();
pthread_spin_unlock(&lock); // open the mutex; other thread will see this change
 //  in ~150 CPU ticks (checked on Pentium4 and Intel Core2 single socket systems);
 // time of the operation itself is of the same order; didn't measure it.
continue_work();
}
1 голос
/ 30 августа 2011

Чтобы сообщить другому процессу о том, что он должен продолжаться, не заставляя отправителя тратить время на вызов ядра, один механизм сразу приходит на ум.Без вызовов ядра все, что может сделать процесс, это изменить память;таким образом, решение - межпроцессная общая память .Как только отправитель пишет в разделяемую память, получатель должен увидеть изменение без каких-либо явных вызовов ядра, и наивный опрос получателя должен работать нормально.

Одна дешевая (но, возможно, недостаточно дешевая) альтернатива - делегирование отправкик вспомогательному потоку в том же процессе, и попросите вспомогательный поток выполнить правильный межпроцессный "выпуск семафора" или вызов записи канала.

0 голосов
/ 20 января 2013

Я понимаю, что вы хотите избежать использования ядра, чтобы избежать накладных расходов, связанных с ядром. Большинство таких издержек связано с переключением контекста. Вот демонстрация одного способа выполнить то, что вам нужно, используя сигналы без вращения и без переключения контекста:

#include <signal.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <iostream>
#include <thread>

using namespace std;

void sigRtHandler(int sig) {
    cout << "Recevied signal" << endl;
}

int main() {
    constexpr static int kIter = 100000;
    thread t([]() {
        signal(SIGRTMIN, sigRtHandler);
        for (int i = 0; i < kIter; ++i) {
            usleep(1000);
        }
        cout << "Done" << endl;
    });
    usleep(1000);   // Give child time to setup signal handler.
    auto handle = t.native_handle();
    for (int i = 0; i < kIter; ++i)
        pthread_kill(handle, SIGRTMIN);
    t.join();
    return 0;
}

Если вы запустите этот код, вы увидите, что дочерний поток продолжает получать SIGRTMIN. Во время выполнения процесса, если вы посмотрите в файле / proc / (PID) / task / * / status для этого процесса, вы увидите, что родительский поток не вызывает переключение контекста из вызова pthread_kill ().

Преимущество этого подхода состоит в том, что ожидающий поток не должен вращаться. Если работа ожидающего потока не зависит от времени, этот подход позволяет вам сэкономить CPU.

...