Принудительное переключение контекста потока - PullRequest
2 голосов
/ 05 декабря 2011

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

#define TRUE  (1==1)
#define FALSE (0==1)

#include <pthread.h>

int acquire(void);
int release(void);
int c_yield(int count);

// Who was the last to acquire the lock
static volatile pthread_t lock_owner;

// Is the lock currently taken
static volatile int lock_taken = FALSE;

/* This variable indicates how many threads are currently waiting for
 * the lock. */
static volatile int lock_wanted = 0;

/* Mutex for protecting access to lock_wanted, lock_owner and
 * lock_taken */
static pthread_mutex_t mutex;

/* Condition even to notify when the lock becomes available */
static pthread_cond_t cond;

void init_lock(void) {
    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);
}

int acquire(void) {
    pthread_mutex_lock(&mutex);
    if(lock_taken) {
        lock_wanted++;
        pthread_cond_wait(&cond, &mutex);
        lock_wanted--;
    } 
    if(lock_taken) {
        pthread_mutex_unlock(&mutex);
        return EPROTO;
    } 
    lock_taken = TRUE;
    lock_owner = pthread_self();
    return pthread_mutex_unlock(&mutex);
}

int release(void) {
    pthread_mutex_lock(&mutex);
    lock_taken = FALSE;
    if(lock_wanted > 0) {
        pthread_cond_signal(&cond);
    }
    return pthread_mutex_unlock(&mutex);
}

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

Эта реализация работает нормально большую часть времени, но если я стресс-тестирую ее с ~ 50 потоками, пытающимися получить и снять блокировку через случайные интервалы, каждыйвремя от времени acqu () возвращает EPROTO, указывая, что кто-то вызвал pthread_cond_signal без предварительной установки lock_taken = FALSE.

Почему это так?Кажется, что процессор иногда не видит новое значение lock_taken, поэтому я уже сделал переменные изменчивыми.Но это все еще происходит ...

1 Ответ

7 голосов
/ 05 декабря 2011
if(lock_taken) {
    lock_wanted++;
    pthread_cond_wait(&cond, &mutex);
    lock_wanted--;
} 

Это должно быть while(lock_taken), а не if.Есть несколько причин, по которым вы можете выйти из pthread_cond_wait, но найти блокировку, занятую другим потоком, к тому времени, когда вы запланировали.Во-первых, если есть ложное пробуждение.Другой случай, если другой поток входит в acquire после того, как мы заблокируем, обнаружит, что блокировка не снята, и получит ее сама, прежде чем этот поток снова получит мьютекс.

Канонический путь будет:

lock_wanted++;
while(lock_taken) pthread_cond_wait(&cond, &mutex);
lock_wanted--;

Избавьтесь от всех volatile s, они вредят производительности и не нужны.Поскольку мьютексы являются достаточной синхронизацией, больше ничего не нужно.(И всем, кто просматривает ваш код, они сообщают, что вы не понимаете синхронизацию потоков, и пытались «добавить их», пока это не сработало.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...