Ожидать на нескольких условных переменных на Linux без ненужных снов?

Firestore поддерживает автономный режим по умолчанию, но не поддерживает await и runTransaction в автономном режиме. Если они присутствуют, то будущее не будет разрешено, поэтому создается впечатление, что Firestore не поддерживает автономный режим. Просто напишите свой код без await вызовов или runTransaction, и вы обнаружите, что ваше приложение поддерживает автономный режим.

Например, в этот раздел кода не будет поддерживать автономный режим, потому что вы await готовитесь к будущему разрешению. Вместо этого, удаление ключевого слова await приведет к обновлению списка в автономном режиме, а когда интернет вернется, база данных также обновится.

27
задан Roman Nikitchenko 23 April 2014 в 22:24
поделиться

3 ответа

Если вы говорите о POSIX-потоках, то я бы рекомендовал использовать единственную переменную условия и количество флагов событий или что-то подобное. Идея состоит в том, чтобы использовать мьютекс peer condvar для защиты уведомлений о событиях. Вам в любом случае нужно проверять наличие события после выхода из cond_wait(). Вот мой достаточно старый код для иллюстрации этого из моего обучения (да, я проверил, что он работает, но, пожалуйста, обратите внимание, что он был подготовлен некоторое время назад и в спешке для новичков).

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

static pthread_cond_t var;
static pthread_mutex_t mtx;

unsigned event_flags = 0;
#define FLAG_EVENT_1    1
#define FLAG_EVENT_2    2

void signal_1()
{
    pthread_mutex_lock(&mtx);
    event_flags |= FLAG_EVENT_1;
    pthread_cond_signal(&var);
    pthread_mutex_unlock(&mtx);
}

void signal_2()
{
    pthread_mutex_lock(&mtx);
    event_flags |= FLAG_EVENT_2;
    pthread_cond_signal(&var);
    pthread_mutex_unlock(&mtx);
}

void* handler(void*)
{
    // Mutex is unlocked only when we wait or process received events.
    pthread_mutex_lock(&mtx);

    // Here should be race-condition prevention in real code.

    while(1)
    {
        if (event_flags)
        {
            unsigned copy = event_flags;

            // We unlock mutex while we are processing received events.
            pthread_mutex_unlock(&mtx);

            if (copy & FLAG_EVENT_1)
            {
                printf("EVENT 1\n");
                copy ^= FLAG_EVENT_1;
            }

            if (copy & FLAG_EVENT_2)
            {
                printf("EVENT 2\n");
                copy ^= FLAG_EVENT_2;

                // And let EVENT 2 to be 'quit' signal.
                // In this case for consistency we break with locked mutex.
                pthread_mutex_lock(&mtx);
                break;
            }

            // Note we should have mutex locked at the iteration end.
            pthread_mutex_lock(&mtx);
        }
        else
        {
            // Mutex is locked. It is unlocked while we are waiting.
            pthread_cond_wait(&var, &mtx);
            // Mutex is locked.
        }
    }

    // ... as we are dying.
    pthread_mutex_unlock(&mtx);
}

int main()
{
    pthread_mutex_init(&mtx, NULL);
    pthread_cond_init(&var, NULL);

    pthread_t id;
    pthread_create(&id, NULL, handler, NULL);
    sleep(1);

    signal_1();
    sleep(1);
    signal_1();
    sleep(1);
    signal_2();
    sleep(1);

    pthread_join(id, NULL);
    return 0;
}
12
ответ дан 28 November 2019 в 05:38
поделиться

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

Активные модули должны быть спроектированы таким образом, чтобы их интерфейсы обеспечивали уведомления о обратном вызове событий через зарегистрированные функции: и, при необходимости, чтобы можно было зарегистрировать несколько обратных вызовов.

Клиент нескольких модулей регистрирует обратный вызов с каждым из них. Все они могут быть направлены в общее место, где они блокируют один и тот же мьютекс, изменяют некоторое состояние, разблокируют и нажимают на одну и ту же переменную условия.

Этот дизайн также дает возможность, что, если объем работы, выполненной в ответ на событие, достаточно мал, возможно, это может быть просто сделано в контексте обратного вызова.

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


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

Суть его в том, что для каждого объекта, которого поток хочет ждать, операция ожидания ставит в очередь интерфейс обратного вызова с этим объектом. Когда объект получает сигнал, он вызывает все свои зарегистрированные обратные вызовы. Пробужденные потоки исключают из очереди все интерфейсы обратного вызова и просматривают некоторые флаги состояния в каждом из них, чтобы увидеть, какие объекты сигнализируют.

4
ответ дан Kaz 28 November 2019 в 05:38
поделиться

Для ожидания нескольких условных переменных существует реализация для Solaris, которую вы можете перенести на Linux, если вам интересно: WaitFor API

2
ответ дан bitek 28 November 2019 в 05:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: