为什么这个pthread代码会挂起?

时间:2015-03-08 16:26:18

标签: c multithreading pthreads

以下代码片段被写入打印偶数,一个线程和奇数与其他线程。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

#define LIMIT 100

int counter;

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

void* function(void *p)
{
    int expected = *(int *)p;
    while(expected < LIMIT) 
    {
        pthread_mutex_lock( &mutex1 );
        while (counter != expected);
            printf("%d\n", counter++);
        expected += 2;
        pthread_mutex_unlock( &mutex1 );
    };
    exit(0);
}

int main(int argc, char *argv[])
{
    pthread_t thread1, thread2;
    int counter = 0;
    int expected_0 = 0, expected_1 = 1;

    pthread_create(&thread1, NULL, function, (void *)&expected_0);
    pthread_create(&thread2, NULL, function, (void *)&expected_1);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

但是当执行时它会显示0的输出并挂起

amit@ubuntu:~$ gcc -pthread even_odd.c 
amit@ubuntu:~$ ./a.out 
0

任何人都可以指出我的情况吗?

还有更好的逻辑来完成这项任务吗?

2 个答案:

答案 0 :(得分:1)

这是互斥锁死锁的经典案例。

让我们看看细节:

在这里,您按顺序创建了两个线程,第一个期望counter为0,第二个期望counter为1.然后每个线程将增加一个并且使另一个线程具有预期值

现在没有必要在线程thread1之前创建线程thread2,然后线程thread1将始终在线程thread2之前执行。这完全取决于您的OS调度程序,并且您对它的控制很少。

因此当thread2thread1之前开始执行时,它将获得mutex1并将开始在while (counter != expected);语句中等待。现在,当thread1开始执行时,mutex1会阻止thread2,因为它已被counter获取。现在,没有人可以更新void* function(void *p) { int expected = *(int *)p; while(expected < LIMIT) { pthread_mutex_lock( &mutex1 ); if (counter == expected) { printf("%d\n", counter++); expected += 2; } pthread_mutex_unlock( &mutex1 ); }; } 。这将导致死锁。

您应该使用以下代码作为线程函数();

{{1}}

这里

  • 每当我想要读取或写入全局时,我都会锁定互斥锁 变量计数器。这可以防止竞争状况。
  • 我正在释放互斥锁,而没有忙于在锁定的互斥锁内等待 相同的变量。这可以防止死锁。

答案 1 :(得分:1)

这里有几个问题。

主要的一点是,当一个线程应该等待counter达到其expected值时,它不会解锁互斥锁以允许另一个线程继续进行。这修复了(添加<stdbool.h>并编译为C99):

void* function(void *p)
{
    int expected = *(int *)p;
    while(expected < LIMIT)
    {
        bool my_turn = false;
        while (!my_turn)
        {
            pthread_mutex_lock( &mutex1 );
            my_turn = (counter == expected);
            pthread_mutex_unlock( &mutex1 );
        }
        pthread_mutex_lock( &mutex1 );
        printf("%d\n", counter++);
        pthread_mutex_unlock( &mutex1 );
        expected += 2;
    };

上面的代码锁定互斥锁以检查计数器,然后再次将其解锁并在计数器未达到预期值时循环,以便另一个线程可以运行。然后再次锁定互斥锁以再次访问counter

另一个问题是调用exit(0)会导致两个线程在其中一个线程完成后立即退出。您可能只想从函数返回:

    return NULL;
}

编写循环的另一种方法是:

        while (true)
        {
            pthread_mutex_lock( &mutex1 );
            if (counter == expected)
                break;
            pthread_mutex_unlock( &mutex1 );
        }
        printf("%d\n", counter++);
        pthread_mutex_unlock( &mutex1 );
        expected += 2;

理解这一点稍微复杂一些,但避免在计数器达到预期值时解锁并重新锁定互斥锁。相反,它会突破循环,使互斥锁被锁定,打印值,然后解锁互斥锁。