[英]Is there a full memory barrier around sem_post(sem_t * sem) and sem_wait(sem_t * sem)?
[英]why dosen't sem_wait() function block even when sem_t value is zero?
我正在尝试为哲学家就餐问题(有五个哲学家)实现一个简单的解决方案,我的解决方案基于以下逻辑:
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]))
}
}
每个哲学家首先思考不到三秒钟
然后,如果右筷子可用,哲学家会拿走它,如果还有左筷子可用,哲学家也会拿走筷子并开始吃不到三秒钟
然后哲学家会放下筷子,让其他哲学家使用
为避免循环等待,对于最后一位哲学家,我将在同一进程中先选择左筷子,然后选择右筷子和 go
这是我基于此逻辑实现的代码:
#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;
}
但是在调试这段代码的过程中发生了一个问题,在sem_wait(&chopstick[num])
之前chopstick[i]
为0
而不是阻塞当前线程,直到筷子可用sem_wait()
继续进行,所以哲学家开始在没有实际筷子的情况下吃饭.
谁能帮我弄清楚我的问题在哪里?
您不能将 void* 转换为 int。 先修复这个错误。
void *philosopher(void * ph_num )
{
int num=(*(int*)ph_num);
...
}
您正在向线程(哲学家)提供循环变量的地址。 并且所有线程(包括主线程)都使用相同的 i 地址。
for(int i = 0; i < THREADS; i++)
//pthread_create(&threads[i], NULL, philosopher, (void *)i); // Wrong cast here too, int cannot be cast to void*
pthread_create(&threads[i], NULL, philosopher, (void *)&i); // True way.
线程参数的可能竞争条件
在那之后,有一个可能的竞争条件。 您将循环变量 i 的地址作为参数传递给线程。 因此,主线程和其他线程(哲学家)在同一个地址上运行。 您的哲学家线程可以在您的主线程创建所有线程后开始运行。 这意味着 i 的值将保持在您定义的 NUMBERS 值。 因此,所有线程都将使用相同的 i 值,而不是使用不同的 i(0,1,2,3....) 值。
为确保所有线程将使用不同的 memory 位置进行索引,请为它们分配不同的 memory:
for(int i = 0; i < THREADS; i++)
{
int* index_for_thread = (int*)calloc(sizeof(int), 1);
*index_for_thread = i;
pthread_create(&threads[i], NULL, philosopher, index_for_thread ); // pass new allocated address to thread.
}
之后,您可以安全地取消引用线程参数,获取值并将其删除。
void *philosopher(void * ph_num )
{
int num=(*(int*)ph_num);
free(ph_num)// free the address, since you allocated in the main.
printf("Philosopher thread %d started to run.. \n", num); // Add this line to be sure about all Philosophers started with true id (num).
}
获取信号量的值
是的,sem_get_value() 为您提供了指定信号量的值。 由于您在不同线程上对同一信号量进行操作,因此获取信号量的值可能无法为您提供真正的值。 调试,相信信号量的“值”不是一个好主意。
Posix Semaphores on Dining Philosopher based Problems
Posix 信号量不能同时对多个信号量进行操作。 如果每个哲学家都选择右叉,他们将等待左叉,这在这种情况下是不可能的。 请考虑使用允许您同时对多个信号量进行操作的 System V 信号量。
您的实现是正确的,您遇到的问题在于调试方法。 如果您使用gdb
,您将仅在一个线程上停止,而该线程的 rest 将继续执行,因此在您检查信号量的时间和您进入下一行的时间之间,其他线程将继续执行并且可以更改您检查的值。
为了有效地调试线程,您需要确保仅调度当前观察到的线程并且线程的 rest 被阻塞。 为此,您需要在线程停止后更改scheduler-locking
。 您可以将其设置为on
或step
,具体取决于您是希望线程完全停止,还是仅在单步操作期间停止(有关更多详细信息,请参阅help set scheduler-locking
)。
一旦线程被锁定,您可以使用info threads
检查线程的 rest 当时正在做什么。 您可以使用thread <<n>>
更改为第 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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.