简体   繁体   中英

Coordinating multiple posix semaphores

What is the best way to juggle multiple threads across two semaphores? I'm learning C and about semaphores, so am working on a sort of dining philosophers inspired scenario.

Two tables (controlled each by a semaphore with 4 max slots each, 8 total to represent plates). One server semaphore to manage 10 customers represented by pthreads. First four customers should go to Table A, 5-8 to Table B, the two take the slots as they become available. Customer finishes eating, goes back into critical region to wait for another seat.

What I believe to have implemented so far is the server semaphore seating the ten customers in one of the 8 seats as they become available. However, the way I have it, each customer goes to eat at Table A, then Table B once then finish as I'm just learning how these work (makes sense now that I look at it).

Is each person eating by themself at the table? Or is this simultaneous...I can't tell. How do i check whether or not a semaphore is blocked, to pass to Table A or B?

Found this other helopful thread , but there were no definitive answers.

UPDATE Had some helpful insight from SO user regarding sem_trywait. Using the oracle man page suggested, I am also attempting to use sem_getvalue. Am I mistaken as to how this is used : 'places the current value of the semaphore pointed to sem into the integer pointed to by sval'. I interpret this as giving me the current number of thread that is attempting to access the sempahore. Would like to use this to let customers back into the table once they've finished (infinite looping the customers back through the table until the program is killed).

Problem is, I now have a deadlock. And am very confused as to why people are sitting down at Table B first.

My code (update : - modified eat function; original code in eat commented out):

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


sem_t server_sem;
int server_pshared;
int server_ret;
int server_count = 8;

sem_t tablea_sem;
int tablea_pshared;
int tablea_ret;
int tablea_count = 4;

sem_t tableb_sem;
int tableb_pshared;
int tableb_ret;
int tableb_count = 4;



//server_ret = serm_open("serverSem", O_CREAT | O_EXCL, 0644, server_count);

int customer_count = 10;
pthread_t customer[10];
//pthread_t plates[8]

int plate_count = 8;
pthread_mutex_t plates;


//void *eat(int n) {
void *eat(void *i) {

    //int n = *((int *) i);
    int n = (int)(intptr_t) i;

  //UPDATED CODE -- see console output 2
  int num_plates;

  sem_wait(&server_sem);

  if (sem_trywait(&tablea_sem) != 0)
  if (errno == EAGAIN) { //no plate at table A available
    //sem_trywait(&tableb_sem)
    sem_wait(&tableb_sem);
    pthread_mutex_lock(&plates);
    plate_count--;
    printf("Customer %d is eating at Table B\n", n);
    //printf("Plate count is %d\n", n);
    sleep(2);
    pthread_mutex_unlock(&plates);
    sem_getvalue(&tableb_sem, &num_plates);
    printf("Num plate %d", num_plates);
    sem_post(&tableb_sem);
    printf("Customer %d is finished eating at Table B\n", n);
  }
  else {
    sem_wait(&tablea_sem);
    pthread_mutex_lock(&plates);
    plate_count--;
    printf("Customer %d is eating at Table A\n", n);
    sleep(2);
    pthread_mutex_unlock(&plates);
    sem_getvalue(&tablea_sem,&num_plates);
    printf("Num plate %d", num_plates);
    sem_post(&tablea_sem);
    printf("Customer %d is finished eating at Table A\n", n);

  }

  //---> Original post code commented out
  // sem_wait(&tablea_sem);
  // pthread_mutex_lock(&plates);
  // plate_count--;
  // printf("Customer %d is eating at Table A\n", n);
  // sleep(2);
  // pthread_mutex_unlock(&plates);
  // sem_post(&tablea_sem);
  // printf("Customer %d is finished eating at Table A\n", n);
  //
  // sem_wait(&tableb_sem);
  // pthread_mutex_lock(&plates);
  // plate_count--;
  // printf("Customer %d is eating at Table B\n", n);
  // //printf("Plate count is %d\n", n);
  // sleep(2);
  // pthread_mutex_unlock(&plates);
  // sem_post(&tableb_sem);
  // printf("Customer %d is finished eating at Table B\n", n);



    return (NULL);

}

int main() {


   server_ret = sem_init(&server_sem, 1, server_count);
   tablea_ret = sem_init(&tablea_sem, 1, tablea_count);
   tableb_ret = sem_init(&tableb_sem, 1, tableb_count);

   //customer = (pthread_t[10] *)malloc(sizeof(customer));

   printf ("starting thread, semaphore is unlocked.\n");

   int i;
   int j;
   int k;


   pthread_mutex_init(&plates,NULL);



   //sem_wait(&server_sem);
   for (j=0;j<customer_count;j++) {
     pthread_create(&customer[j],NULL,(void *)eat,(void *) (intptr_t) j);
   }

   for(k=0;k<customer_count;k++) {
      pthread_join(customer[k],NULL);
      printf("Joining thread %d\n", k);
   }


   pthread_mutex_destroy(&plates);

   //sem_post(&server_sem);


   return 0;
   }

Console output :

niu@niu-vb:~/Documents/CSU_OS$ ./diner
starting thread, semaphore is unlocked.
Customer 6 is eating at Table A
Customer 6 is finished eating at Table A
Customer 6 is eating at Table B
Customer 6 is finished eating at Table B
Customer 8 is eating at Table A
Customer 8 is finished eating at Table A
Customer 8 is eating at Table B
Customer 8 is finished eating at Table B
Customer 7 is eating at Table A
Customer 7 is finished eating at Table A
Customer 7 is eating at Table B
Customer 7 is finished eating at Table B
Customer 9 is eating at Table A
Customer 9 is finished eating at Table A
Customer 9 is eating at Table B
Customer 9 is finished eating at Table B
Customer 5 is eating at Table A
Customer 5 is finished eating at Table A
Customer 5 is eating at Table B
Customer 5 is finished eating at Table B
Customer 4 is eating at Table A
Customer 4 is finished eating at Table A
Customer 4 is eating at Table B
Customer 4 is finished eating at Table B
Customer 3 is eating at Table A
Customer 3 is finished eating at Table A
Customer 3 is eating at Table B
Customer 3 is finished eating at Table B
Customer 2 is eating at Table A
Customer 2 is finished eating at Table A
Customer 2 is eating at Table B
Customer 2 is finished eating at Table B
Customer 1 is eating at Table A
Customer 1 is finished eating at Table A
Customer 1 is eating at Table B
Customer 1 is finished eating at Table B
Customer 0 is eating at Table A
Customer 0 is finished eating at Table A
Customer 0 is eating at Table B
Customer 0 is finished eating at Table B
Joining thread 0
Joining thread 1
Joining thread 2
Joining thread 3
Joining thread 4
Joining thread 5
Joining thread 6
Joining thread 7
Joining thread 8
Joining thread 9

Console output 2 :

Starting program: /home/niu/Documents/CSU_OS/diner 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
starting thread, semaphore is unlocked.
[New Thread 0x7ffff77f6700 (LWP 20306)]
[New Thread 0x7ffff6ff5700 (LWP 20307)]
[Thread 0x7ffff77f6700 (LWP 20306) exited]
[New Thread 0x7ffff67f4700 (LWP 20308)]
[Thread 0x7ffff6ff5700 (LWP 20307) exited]
[New Thread 0x7ffff5ff3700 (LWP 20309)]
[Thread 0x7ffff67f4700 (LWP 20308) exited]
[New Thread 0x7ffff57f2700 (LWP 20310)]
Customer 4 is eating at Table B
[Thread 0x7ffff5ff3700 (LWP 20309) exited]
[New Thread 0x7ffff4ff1700 (LWP 20311)]
[New Thread 0x7ffff47f0700 (LWP 20312)]
[New Thread 0x7ffff3fef700 (LWP 20313)]
[New Thread 0x7ffff37ee700 (LWP 20314)]
[New Thread 0x7ffff2fed700 (LWP 20315)]
Joining thread 0
Joining thread 1
Joining thread 2
Joining thread 3
Num plate 1Customer 4 is finished eating at Table B
Customer 5 is eating at Table B
Joining thread 4
[Thread 0x7ffff57f2700 (LWP 20310) exited]
Num plate 2Customer 5 is finished eating at Table B
Customer 6 is eating at Table B
Joining thread 5
[Thread 0x7ffff4ff1700 (LWP 20311) exited]
Num plate 3Customer 6 is finished eating at Table B
Joining thread 6
[Thread 0x7ffff47f0700 (LWP 20312) exited]

It's tricky to use a semaphore to control access to a resource that a thread might want. You can use sem_trywait ; to avoid switching back and forth between Tables A and B, you can add a semaphore protecting the two tables together (with an initial count of 8, of course).

Then each customer thread, in a loop, downs the outer semaphore and tries to down A's semaphore, downing B's semaphore if that fails. (You could try them in the other order sometimes if you didn't want to favor A.) Then it ups its choice and the outer semaphore.

Note that this is more or less what you have with server_sem , whose purpose isn't clear in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM