为什么此互斥代码无法正常工作?

时间:2019-03-05 01:21:24

标签: c++ pthreads mutex

关于该主题的帖子和答案一团糟,但似乎没有一个模型可以很好地模拟我的问题。翻遍谷歌搜索并搜索stackoverflow之后,我不太清楚问题的答案。

我有两个线程,一个主线程和一个从属线程。从属服务器需要先等待主服务器,然后才能进行到特定点,因此我已经创建了一个互斥锁作为全局变量:

pthread_mutex_t lock;

然后在对主线程进行初始化的过程中,很久之前,从属线程才有机会访问它,我将其锁定:

在主线程中初始化:

pthread_mutex_lock(&lock)

然后在从站中,当它等待主站时,我这样做:

从属必须在这里等待:

pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);

同时,回到主服务器,当它有时间“释放”被阻塞等待的从服务器时,我有这个提示:

pthread_mutex_unlock(&lock);
pthread_mutex_lock(&lock);

(注意:主机的锁定/解锁顺序相反。)

基于对互斥锁的理解,我发现从属设备将锁定锁,并卡在那儿等待主设备将其解锁,然后立即再次将其锁定。就时间而言,从站需要很长时间(保证)才能再次回到这里,因此主站将有很多时间来重新锁定它。同样,主服务器不会再回到这里了,我们也不必担心主服务器或从服务器将另一个打回到这些检查点。

当它没有按预期工作时,我扔了一些printf来确认主机已解锁,然后在从机可以解锁之前重新锁定互斥锁。我的理解是,从属服务器在主机到达那里进行解锁和(重新)锁定之前就已经请求了锁定,并且无论主机与主机之间的解锁和(重新)锁定之间的时间有多短,从属设备仍然应该能够锁定互斥锁,因为他已经在“排队”等待。

但是,我看到的是,主服务器将互斥锁解锁,然后立即重新锁定它,即使从属计算机已耐心地等待其锁定它的机会。

这是其中包含printf的代码,并生成了输出:

从属:

printf("slave thread starting lock sequence\n");fflush(stdout);
pthread_mutex_lock(&lock);
printf("slave thread intra lock sequence\n");fflush(stdout);
pthread_mutex_unlock(&lock);
printf("slave thread completed lock sequence\n");fflush(stdout);

主人:

printf("master thread starting lock sequence\n");fflush(stdout);
pthread_mutex_unlock(&lock);
printf("master thread intra lock sequence\n");fflush(stdout);
pthread_mutex_lock(&lock);
printf("master thread completed lock sequence\n");fflush(stdout);

现在这是我所看到的输出:

从属线程启动锁定顺序

...然后一段时间(几秒钟),而奴隶被阻止,最后出现:

主线程启动锁定顺序

主线程内部锁定顺序

主线程已完成锁定顺序

与此同时,奴隶没有任何进一步的进步,奴隶永远被封锁。我希望他能防止主人重新锁定,并且应该吐出他的指纹,表明他已经前进了。此输出清楚地表明,受阻的奴隶即使有耐心排队等候,也没有机会锁定互斥体。

那我对互斥锁和锁定/解锁缺少什么?

-gt-

3 个答案:

答案 0 :(得分:0)

如您对问题的评论中所述,pthread互斥体不能保证公平。

此作业的正确工具是共享标志变量,该变量受互斥锁保护并等待使用条件变量:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int flag = 0;

从站等待标志:

pthread_mutex_lock(&lock);
while (!flag)
    pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);

当主机要释放从机时,它将设置标志并向条件变量发送信号:

pthread_mutex_lock(&lock);
flag = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);

(请注意,互斥体在pthread_cond_wait()内被阻塞时不被保存)。

答案 1 :(得分:0)

  

当它没有按预期工作时,我扔了一些printf来确认主机已解锁,然后在从机可以解锁之前重新锁定互斥锁。我的理解是,从属服务器在主机到达那里进行解锁和(重新)锁定之前就已经请求了锁定,并且无论主机与主机之间的解锁和(重新)锁定之间的时间有多短,从属设备仍然应该能够锁定互斥锁,因为他已经在“排队”等待。

没有理由公平对待线程,如果您虐待线程,它们不会提出工会申诉。但是有一个理由使整个系统尽可能快地完成尽可能多的工作。停止一个线程来启动另一个线程会对性能产生严重的负面影响,因为新线程在所有缓存均处于冷状态时开始运行,并且当您切换回去时也会发生同样的事情。

确保您的代码在执行时能够完成您希望做的工作是您的工作。调度程序会尝试在不时地点头以求公平的前提下尽快完成工作。

答案 2 :(得分:0)

也许您可以在信号量后对问题进行建模,我发现这通常更易于理解和实施。类似于下面的伪代码示例。

//before create slave thread, create semaphore first
sem_t data_ready, slave_done;
sem_init(&data_ready, 0);
sem_init(&slave_done, 0);

在主线程中:

//master do something AAA
sem_post(data_ready);
//master do something BBB
sem_wait(slave_done);  //master may sleep here, and maybe interrupted by signal, need to handle that
//master do something CCC

在从属线程中:

//slave do something DDD
sem_wait(&data_ready); //slave may sleep here, and maybe interrupted by signal, need to handle that
//slave do something EEE
sem_post(&slave_done);
//slave do something FFF

您可能会发现执行顺序应为AAA [BBB / DDD] EEE [FFF / CCC],这确保了master-> AAA在slave-> EEE之前,而后者在master-> CCC之前运行。