【问题标题】:why dosen't sem_wait() function block even when sem_t value is zero?为什么即使 sem_t 值为零,sem_wait() 函数也不阻塞?
【发布时间】:2022-01-20 11:33:08
【问题描述】:

我正在尝试为哲学家就餐问题(有五个哲学家)实施一个简单的解决方案,我的解决方案基于这个逻辑:

sem_t S[philosophers_number]
for each philosopher
{
    while(TRUE)
    {
        if(current philosopher number != last philosopher)
        {
            thinking()

            //i is number of current philosopher
            sem_wait(take_chopstick(S[(i+1) % philosophers_number])) // right chopstick
            sem_wait(take_chopstick(S[i])) // left chopstick

            eat()

            sem_post(put_chopstick(S[(i+1) % philosophers_number]))
            sem_post(put_chopstick(S[i]))
        }
        else
        {
            thinking()

            //i is number of current philosopher
            sem_wait(take_chopstick(S[i])) // left chopstick
            sem_wait(take_chopstick(S[(i+1) % philosophers_number])) // right chopstick

            eat()

            sem_post(put_chopstick(S[i]))          
            sem_post(put_chopstick(S[(i+1) % philosophers_number]))  
        }
}

每个哲学家首先思考不到三秒钟

如果右边的筷子可用,哲学家会拿走它,如果还有左边的筷子可用,哲学家也会拿走筷子,然后开始吃不到三秒钟

然后哲学家会放下筷子,让其他哲学家使用

为了避免循环等待,对于最后一位哲学家,我会先选择左筷子,然后选择右筷子,然后继续进行相同的过程

这是我基于这个逻辑实现的代码:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>

#define THREADS 5
sem_t chopstick[THREADS];

void thinking(int ph_num)
{
    printf("philosopher %d is thinking\n", ph_num);
    int t = rand() % 3;
    sleep(t);// up to 3 secs thinking
}

void eat(int ph_num)
{
    printf("philosopher %d is eating\n", ph_num);
    int t = rand() % 3;
    sleep(t);// up to 3 secs eating
}

void *philosopher(void * ph_num )
{
    int num=(int)ph_num;
    while(1)
    {
        if(num < THREADS - 1)
        {
            thinking(num);
            
            //pick up right chopstick
            sem_wait(&chopstick[(num + 1) % THREADS]);

            //to make deadlocks absolutly happen, wait 1 sec then pickup left chopstick
            sleep(1);
            
            //pick up left chopstick
            sem_wait(&chopstick[num]);
        
            eat(num);   

            //put down right chopstick
            sem_post(&chopstick[(num + 1) % THREADS]);

            //put down left chopstick
            sem_post(&chopstick[num]);
        }

        else // last one pick left chopstick first, instead of right one to avoid cyclic wait
        {
                        thinking(num);

                        //pick up left chopstick
                        sem_wait(&chopstick[num]);

                        //to make deadlocks absolutly happen, wait 1 sec then pickup left chopstick 
                        sleep(1); 
                        
                        //pick up right chopstick
                        sem_wait(&chopstick[(num + 1) % THREADS]);

                        eat(num);

                        //put down left chopstick
                        sem_post(&chopstick[num]);

                        //put down right chopstick
                        sem_post(&chopstick[(num + 1) % THREADS]);
        }
    }

    pthread_exit((void *)num);
}

int main ()
{
    for(int i = 0; i < THREADS; i++)
    {
        sem_init(&chopstick[i],0,1);
    }

    pthread_t threads[THREADS];
    
    for(int i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, philosopher, (void *)i);
    
    for(int i = 0; i < THREADS; i++)
        pthread_join(threads[i],NULL);
    return 0;
}

但是在调试这段代码时发生了一个问题,其中chopstick[i]sem_wait(&amp;chopstick[num]) 之前是0,而不是阻塞当前线程,直到有筷子可用sem_wait() 继续进行,所以哲学家开始在没有实际筷子的情况下吃饭.

谁能帮我找出问题出在哪里?

【问题讨论】:

  • 如何查看信号量的值?我不建议您使用信号量的值进行调试,因为线程可以在您检查该值之后等待/发布,因此尽管结构为真,但该值可能是错误的。附带说明一下,Posix 信号量与此问题不兼容,因为它们不能让您同时对至少两个信号量进行操作。
  • @tango-1 谢谢,但实际上我必须使用 posix 信号量来完成这项任务,并且我正在使用 sem_getvalue() 获取信号量的值,但我不确定这是否是正确的方法。跨度>

标签: c multithreading operating-system semaphore dining-philosopher


【解决方案1】:

您的实现是正确的,您遇到的问题在于调试方法。如果您使用gdb,您将只在一个线程上停止,而其余线程将继续执行,因此在您检查信号量和进入下一行之间,其他线程将继续执行执行,并且可以更改您检查的值。

为了有效地调试线程,您需要确保只有当前观察到的线程被调度,其余线程被阻塞。为此,您需要在线程停止后更改scheduler-locking。您可以将其设置为onstep,具体取决于您是希望线程完全停止,还是仅在单步操作期间停止(有关详细信息,请参阅help set scheduler-locking)。

一旦线程被锁定,您可以使用info threads 检查其余线程当时正在做什么。可以使用thread &lt;&lt;n&gt;&gt;切换到第n个线程,使用where查看线程栈。

这里是调度程序设置为step 的示例。您可以看到在next 命令上只进行了一个线程。

(gdb) b 37
Breakpoint 1 at 0x1388: file test003.c, line 37.
(gdb) r
Starting program: /home/jordan/Development/tmptest/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7d90700 (LWP 4002538)]
philosopher 0 is thinking
[New Thread 0x7ffff758f700 (LWP 4002539)]
philosopher 1 is thinking
[New Thread 0x7ffff6d8e700 (LWP 4002540)]
philosopher 2 is thinking
[2] picking 3
[New Thread 0x7ffff658d700 (LWP 4002541)]
[Switching to Thread 0x7ffff6d8e700 (LWP 4002540)]

Thread 4 "a.out" hit Breakpoint 1, philosopher (ph_num=0x2) at test003.c:37
37              sem_wait(&chopstick[(num + 1) % THREADS]);
(gdb) set scheduler-locking step
(gdb) info threads
  Id   Target Id                                   Frame
  1    Thread 0x7ffff7d91740 (LWP 4002534) "a.out" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
  2    Thread 0x7ffff7d90700 (LWP 4002538) "a.out" 0x00007ffff7e743bf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
    req=req@entry=0x7ffff7d8fe60, rem=rem@entry=0x7ffff7d8fe60) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
  3    Thread 0x7ffff758f700 (LWP 4002539) "a.out" 0x00007ffff7e743bf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
    req=req@entry=0x7ffff758ee60, rem=rem@entry=0x7ffff758ee60) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
* 4    Thread 0x7ffff6d8e700 (LWP 4002540) "a.out" philosopher (ph_num=0x2) at test003.c:37
  5    Thread 0x7ffff658d700 (LWP 4002541) "a.out" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
(gdb) n
38              printf("[%i] picked %i\n", num, (num + 1) % THREADS);
(gdb) info threads
  Id   Target Id                                   Frame
  1    Thread 0x7ffff7d91740 (LWP 4002534) "a.out" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
  2    Thread 0x7ffff7d90700 (LWP 4002538) "a.out" 0x00007ffff7e743bf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
    req=req@entry=0x7ffff7d8fe60, rem=rem@entry=0x7ffff7d8fe60) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
  3    Thread 0x7ffff758f700 (LWP 4002539) "a.out" 0x00007ffff7e743bf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
    req=req@entry=0x7ffff758ee60, rem=rem@entry=0x7ffff758ee60) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
* 4    Thread 0x7ffff6d8e700 (LWP 4002540) "a.out" philosopher (ph_num=0x2) at test003.c:38
  5    Thread 0x7ffff658d700 (LWP 4002541) "a.out" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78

如你所见,在执行下一个之后,我仍然在同一个线程上,其他线程没有进展。

我已经修改了代码以使正在发生的事情更加明显,这是我使用的代码:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>

#define THREADS 5

sem_t chopstick[THREADS];

void thinking(int ph_num)
{
    printf("philosopher %d is thinking\n", ph_num);
    int t = rand() % 3;
    sleep(t);// up to 3 secs thinking
}

void eat(int ph_num)
{
    printf("philosopher %d is eating\n", ph_num);
    int t = rand() % 3;
    sleep(t);// up to 3 secs eating
}

void *philosopher(void * ph_num )
{
    int num=(int)ph_num;
    while(1)
    {
        if(num < THREADS - 1)
        {
            thinking(num);

            //pick up right chopstick
            printf("[%i] picking %i\n", num, (num + 1) % THREADS);
            sem_wait(&chopstick[(num + 1) % THREADS]);
            printf("[%i] picked %i\n", num, (num + 1) % THREADS);

            //to make deadlocks absolutly happen, wait 1 sec then pickup left chopstick
            //sleep(1);

            //pick up left chopstick
            printf("[%i] picking %i\n", num, num);
            sem_wait(&chopstick[num]);
            printf("[%i] picked %i\n", num, num);

            eat(num);

            //put down right chopstick
            printf("[%i] put %i\n", num, (num + 1) % THREADS);
            sem_post(&chopstick[(num + 1) % THREADS]);

            //put down left chopstick
            printf("[%i] put %i\n", num, num);
            sem_post(&chopstick[num]);
        }

        else // last one pick left chopstick first, instead of right one to avoid cyclic wait
        {
            thinking(num);

            //pick up left chopstick
            printf("[%i] picking %i\n", num, num);
            sem_wait(&chopstick[num]);
            printf("[%i] picked %i\n", num, num);

            //to make deadlocks absolutly happen, wait 1 sec then pickup left chopstick
            //sleep(1);

            //pick up right chopstick
            printf("[%i] picking %i\n", num, num+1);
            sem_wait(&chopstick[(num + 1) % THREADS]);
            printf("[%i] picked %i\n", num, num+1);

            eat(num);

            //put down left chopstick
            printf("[%i] put %i\n", num, num);
            sem_post(&chopstick[num]);

            //put down right chopstick
            printf("[%i] put %i\n", num, (num + 1) % THREADS);
            sem_post(&chopstick[(num + 1) % THREADS]);
        }
    }

    pthread_exit((void *)num);
}

int main ()
{
    for(int i = 0; i < THREADS; i++)
    {
        sem_init(&chopstick[i],0,1);
    }

    pthread_t threads[THREADS];

    for(int i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, philosopher, (void *)i);

    for(int i = 0; i < THREADS; i++)
        pthread_join(threads[i],NULL);
    return 0;
}

【讨论】:

  • 这对我很有帮助,谢谢,我在调试多线程程序时确实遇到了一些问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
相关资源
最近更新 更多