Лидер / последователь pthread реализации на сервере - PullRequest
0 голосов
/ 04 июня 2018

Я пытаюсь реализовать базовый сервер на основе архитектуры параллелизма лидера / последователя.Первоначально я пытался просто создать пул потоков и создать очередь из потоков, но после прочтения этого ответа ( Объяснение шаблона "Лидер / Последователь" ) я чувствую, что мой алгоритм неверен, потому что выбранный ответ требуетчто мьютекс не нужен.

Идея, стоящая за лидером / последователем, заключается в том, что вы инициализируете пул потоков, и один поток служит потоком «лидера», а остальные являются потоками «последователя».На сервере поток-лидер будет прослушивать входящие соединения, а поток-подписчик - все остальные.Когда поток лидера обнаруживает входящее соединение, один из оставшихся потоков последователей будет переведен в новый поток лидера, в то время как бывший лидер принимает соединение и обслуживает запрос.Когда бывший ведущий поток завершил обработку запроса, он становится покоящимся следящим потоком.

Однако я не вижу способа реализовать это без использования мьютекса и переменной условия.В настоящее время моя реализация использует пул потоков и просто позволяет каждому потоку принимать новые соединения в мьютексе.

Может кто-нибудь дать объяснение высокого уровня реализации лидера / последователя?

Вот некоторый код, которыйЯ написал о моей текущей реализации.

#define THREAD_COUNT 10
pthread_mutex_t request_tx;

int main(int argc, char* argv[])
{
    pthread_t threadA[THREAD_COUNT];
    pthread_mutex_init(&request_tx, NULL);

    //initialize server socket stuff

    for (int i = 0; i < THREAD_COUNT; ++i)
    {
        pthread_create(&threadA[i], NULL, rest, NULL);
    }

    for (int i = 0; i < THREAD_COUNT; ++i)
        pthread_join(threadA[i], NULL);

    return 0;
}

void* rest(void* kargs)
{
    int client_fd;
    struct sockaddr_in cli_addr;
    socklen_t sin_t = sizeof(cli_addr);
    while (1)
    {
        pthread_mutex_lock(&request_tx);
        client_fd = accept(server_fd, (struct sockaddr*) &cli_addr, &sin_t);
        if (client_fd > 0)
            serve(client_fd);
        else
            pthread_mutex_unlock(&request_tx);
    }
}

void serve(int client_fd)
{
    pthread_mutex_unlock(&request_tx);
    // serve request here
    ...
}

1 Ответ

0 голосов
/ 04 июня 2018

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

Ваш пример немного вводит в заблуждение, поскольку шаблон лидер / последователи обычно используется для распределения отдельных запросов по нескольким соединениям в пул потоков,не связи.На этом этапе все становится значительно сложнее, потому что подписчик, который завершил обработку, должен сказать лидеру, что новое соединение нуждается в наблюдении.Если вы ограничены стандартными интерфейсами POSIX, это может быть довольно сложно.(С epoll должна быть возможность передать большую часть сложности ядру.)

В общем, я бы с осторожностью принял шаблоны обработки событий 90-х годов.С тех пор архитектура значительно изменилась: машины NUMA больше не редкость, системы могут достаточно удобно обрабатывать десятки тысяч потоков в рамках одного процесса, а также доступны другие интерфейсы обработки событий, такие как epoll.

...