在我的测试程序中,我启动了两个线程,每个线程只执行以下逻辑:
1) pthread_mutex_lock()
2) sleep(1)
3) pthread_mutex_unlock()
但是,我发现经过一段时间后,两个线程中的一个将永远阻塞pthread_mutex_lock(),而另一个线程正常工作。这是一种非常奇怪的行为,我认为这可能是一个潜在的严重问题。通过Linux手册,获取pthread_mutex_t时不禁止sleep()。所以我的问题是:这是一个真正的问题,还是我的代码中有任何错误?
以下是测试程序。在代码中,第一个线程的输出定向到stdout,而第二个线程定向到stderr。所以我们可以检查这两个不同的输出,看看线程是否被阻塞。
我在linux内核(2.6.31)和(2.6.9)上测试过它。两个结果都是一样的。
//======================= Test Program ===========================
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#define THREAD_NUM 2
static int data[THREAD_NUM];
static int sleepFlag = 1;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static void * threadFunc(void *arg)
{
int* idx = (int*) arg;
FILE* fd = NULL;
if (*idx == 0)
fd = stdout;
else
fd = stderr;
while(1) {
fprintf(fd, "\n[%d]Before pthread_mutex_lock is called\n", *idx);
if (pthread_mutex_lock(&mutex) != 0) {
exit(1);
}
fprintf(fd, "[%d]pthread_mutex_lock is finisheded. Sleep some time\n", *idx);
if (sleepFlag == 1)
sleep(1);
fprintf(fd, "[%d]sleep done\n\n", *idx);
fprintf(fd, "[%d]Before pthread_mutex_unlock is called\n", *idx);
if (pthread_mutex_unlock(&mutex) != 0) {
exit(1);
}
fprintf(fd, "[%d]pthread_mutex_unlock is finisheded.\n", *idx);
}
}
// 1. compile
// gcc -o pthread pthread.c -lpthread
// 2. run
// 1) ./pthread sleep 2> /tmp/error.log # Each thread will sleep 1 second after it acquires pthread_mutex_lock
// ==> We can find that /tmp/error.log will not increase.
// or
// 2) ./pthread nosleep 2> /tmp/error.log # No sleep is done when each thread acquires pthread_mutex_lock
// ==> We can find that both stdout and /tmp/error.log increase.
int main(int argc, char *argv[]) {
if ((argc == 2) && (strcmp(argv[1], "nosleep") == 0))
{
sleepFlag = 0;
}
pthread_t t[THREAD_NUM];
int i;
for (i = 0; i < THREAD_NUM; i++) {
data[i] = i;
int ret = pthread_create(&t[i], NULL, threadFunc, &data[i]);
if (ret != 0) {
perror("pthread_create error\n");
exit(-1);
}
}
for (i = 0; i < THREAD_NUM; i++) {
int ret = pthread_join(t[i], (void*)0);
if (ret != 0) {
perror("pthread_join error\n");
exit(-1);
}
}
exit(0);
}
这是输出:
在程序启动的终端上:
root@skyscribe:~# ./pthread sleep 2> /tmp/error.log
[0]Before pthread_mutex_lock is called
[0]pthread_mutex_lock is finisheded. Sleep some time
[0]sleep done
[0]Before pthread_mutex_unlock is called
[0]pthread_mutex_unlock is finisheded.
...
在另一个终端上查看文件/tmp/error.log
root@skyscribe:~# tail -f /tmp/error.log
[1]Before pthread_mutex_lock is called
并没有从/tmp/error.log
输出新行答案 0 :(得分:5)
这是使用互斥锁的错误方法。线程不应该持有互斥锁的时间超过它不拥有它的时间,特别是如果它在按住互斥锁时休眠。锁定互斥锁没有FIFO保证(出于效率原因)。
更具体地说,如果线程1在线程2等待它时解锁互斥锁,则它使线程2可运行,但这不会强制调度程序抢占线程1或使线程2立即运行。最有可能的是,它不会因为线程1最近睡过了。当线程1随后到达pthread_mutex_lock()
调用时,通常会允许它立即锁定互斥锁,即使有一个线程正在等待(并且实现可以知道它)。当线程2在此之后唤醒时,它会发现互斥锁已经锁定并重新进入休眠状态。
最好的解决方案是不要长时间持有互斥锁。如果无法做到这一点,请考虑将需要锁定的操作移动到单个线程(不需要锁定)或使用条件变量唤醒正确的线程。
答案 1 :(得分:1)
既没有问题,也没有代码中的错误,而是缓冲和调度效果的组合。在此处添加fflush
:
fprintf (fd, "[%d]pthread_mutex_unlock is finisheded.\n", *idx);
fflush (fd);
并运行
./a.out >1 1.log 2> 2.log &
你会看到两个线程的进展相当平等。
编辑:和上面提到的@jilles一样,互斥锁应该是短等待锁定,而 long waits 就像条件变量等待,等待I / O或睡眠。这就是为什么互斥体也不是取消点。