我希望能够强制从一个线程切换到另一个线程。因此,我实现了以下锁定过程:
#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个线程对它进行压力测试,试图以随机的间隔获取和释放锁,每隔一段时间,acquire()就会返回EPROTO
,表示有人在没有先设置pthread_cond_signal
的情况下调用了lock_taken = FALSE
。
为什么?似乎CPU有时看不到lock_taken
的新值,这就是我已经使变量变为volatile的原因。但它仍在发生......
答案 0 :(得分:7)
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
,它们会损害性能并且不需要。因为互斥量足够同步,所以不需要其他任何东西。 (并且,对于任何查看您的代码的人来说,他们都表示您不理解线程同步,并试图将它们“洒入”,直到它恰好正常工作。)