Как сериализовать асинхронные разделы кода pthreads - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть несколько упреждающих (асинхронных) потоков (TA), которым требуется временный доступ к общему ресурсу. Это типичное использование для pthread_mutex.

Однако у меня есть несколько вещей, которые усложняют это:

  • У меня мало контроля над полным кодом, потому что я предоставляю только набор функций для упрощения процедуры.

  • Существует набор «основных» потоков (TM), над которыми я мало контролирую и которые используют частный мьютекс для доступа к указанному общему ресурсу. Они используют частный планировщик, который управляет этими потоками, таким образом, что в любой момент разрешено запускать только один, эффективно действуя совместно этими потоками.

  • Любой из этих основных потоков (TM) может вызвать запуск асинхронных потоков (TA). Это все часть кода программиста, которому я хочу предоставить дополнительные функции.

Мне нужно подождать, пока эти потоки ТМ не перейдут в «безопасное» состояние, когда я смогу разрешить потокам ТА получить доступ к общему ресурсу. У меня есть способ сделать это.

Таким образом, идея состоит в том, чтобы приостановить (заблокировать) все потоки TA до тех пор, пока это безопасное состояние не будет достигнуто в любом из потоков TM, затем приостановить этот поток TM, позволить каждому из потоков TA запускаться один за другим, и возобновить ветку ТМ, как только все будет готово.

Когда потоки ТМ достигнут указанного безопасного состояния, будет вызвана моя функция shared_resource_is_safe().

Кроме того, программист должен будет вызывать мои функции acquire_access() и surrender_access() до и после доступа к общему ресурсу.

Итак, мне нужно реализовать три функции, и я борюсь с использованием мьютексов и / или семафоров для достижения своей цели.

Вот что я придумал:

dispatch_semaphore_t semaphore;
pthread_mutex_t mutex;
int is_safe = 0;

void setup_once() {
    semaphore = dispatch_semaphore_create (0);
    pthread_mutex_init (&mutex, NULL);
}

void acquire_access() {
    pthread_mutex_lock (&mutex);
    dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER);
    assert(is_safe);
}

void surrender_access() {
    pthread_mutex_unlock (&mutex);
}

void shared_resource_is_safe() {
    // this shall resume thread that's called acquire_access()
    is_safe = 1;
    while (dispatch_semaphore_signal (semaphore) != 0) {
        // Wait until the signaled thread
        // has called surrender_access()
        pthread_mutex_lock (&mutex);
        pthread_mutex_unlock (&mutex);
    }
    is_safe = 0;
}

Семафор используется для любого потока, вызывающего acquire_access() для ожидания shared_resource_is_safe().

Мьютекс должен убедиться, что все асинхронные потоки ожидают shared_resource_is_safe(), позволяя ему работать.

Это не работает надежно, хотя. Я сталкиваюсь со случаями, когда утверждение для is_safe не выполняется в асинхронных потоках, то есть основной поток не ожидает асинхронных потоков для вызова surrender_access(). Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Ответ mevets идентифицирует вашу ошибку. Как уже упоминалось, вы можете решить это с помощью условных переменных:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_safe = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_waiters = PTHREAD_COND_INITIALIZER;
int is_safe = 0;
long waiters = 0;

void acquire_access(void)
{   
    pthread_mutex_lock(&mutex);
    waiters++;
    while (!is_safe)
        pthread_cond_wait(&cond_safe, &mutex);
}

void surrender_access(void)
{   
    waiters--;
    if (!waiters)
        pthread_cond_signal(&cond_waiters);
    pthread_mutex_unlock(&mutex);
}

void shared_resource_is_safe(void)
{   
    pthread_mutex_lock(&mutex);
    if (waiters)
    {
        is_safe = 1;
        pthread_cond_broadcast(&cond_safe);
        while (waiters)
            pthread_cond_wait(&cond_waiters, &mutex);
        is_safe = 0;
    }
    pthread_mutex_unlock(&mutex);
}
0 голосов
/ 05 сентября 2018

когда dispatch_semaphore_signal () возвращает 0 (чтобы указать, что от него никто не ждет), он увеличивает семафор, поэтому следующий dispatch_semaphore_wait () получит семафор без ожидания.

То, что вы хотите - это переменная условия, которую диспетчеризация не предоставляет. Можно построить один, используя семафор [т.е. вести отдельный счетчик сколько проснуться под защитой мьютекса], но на этом этапе вы можете подумать, не копаете ли вы более глубокую яму.

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

...