简体   繁体   中英

Using a global struct with pthreads in C

I'm trying to simulate clients going into a bank and being served by tellers over a period of time. I am using a thread to determine if a client came into the bank via an arrival rate and am using multiple threads to represent tellers with an s service rate. I am using a struct to hold the period of time, arrival rate, service rate, number of tellers, and the customer waiting queue. I am attempting to share that struct between all threads but get "dereferencing pointer" errors and "request for member x in something not a structure" error.

//Used to store information input by user and the customer queue
struct shiftInfo {
    int tellers;
    int serviceTime;
    int simTime;
    int sampleInterval;
    int threadID;
    float arrivalRate;
    int Q[]; //Customer Queue
};

The information is passed from into the struct from the command line via

    struct shiftInfo *Q = malloc(sizeof(struct shiftInfo) + (maxCust*sizeof(int)));
    struct shiftInfo info;
    info.simTime = atoi(argv[1]);
    info.arrivalRate = atof(argv[2]);
    info.tellers = atoi(argv[3]);
    info.serviceTime = atoi(argv[4]);
    info.sampleInterval = atoi(argv[5]);
    //Initiates Q to 0
    for (int i = 0; i < maxCust; i++)
        info.Q[i] = 0;

The teller, timer, and customer threads are created and terminated by a main thread

  //Manager thread is main thread
  pthread_t manager;
  iret = pthread_create(&manager, NULL, mainThread, (void *)&info);
  if (iret){
    printf("ERROR: return code %d\n", iret);
    exit(-1);
    }

To keep this short I'm going to only ask about the timer thread and hopefully apply the answer to the other threads. The timer thread is created in the main thread by:

  int status;
  struct shiftInfo *I = info; 
  pthread_t time;
      status = pthread_create(&time, NULL, timer, (void *)info);
      if (status){
        printf("ERROR CODE: %d\n", status);
        exit(-1);
    }

And the timer thread function is:

void *timer(void *info){
    int timeRemaining = info -> simTime;
    while(timeRemaining){
        sleep(1);
        timeRemaining--;
        }
}

For "int timeRemaining = info -> simTime;" I get the warning "dereferencing void pointer" and the error request for member âsimTimeâ in something not a structure or union. Any advice would be appreciate.

Also, I create the customer thread identically to the way I create the timer thread but receive a warning (warning: passing argument 3 of âpthread_createâ makes pointer from integer without a cast [enabled by default]) that I do not receive when creating any other thread, what causes that?

pthread_t customer;
    status = pthread_create(&customer, NULL, customer, (void *)info);
        if (status){
            printf("ERROR CODE: %d\n", status);
            exit(-1);
        }

There's quite a few mistakes and typos you made. The first mistake is here:

struct shiftInfo *Q = malloc(sizeof(struct shiftInfo) + (maxCust*sizeof(int)));
struct shiftInfo info;
info.simTime = atoi(argv[1]);
info.arrivalRate = atof(argv[2]);
info.tellers = atoi(argv[3]);
info.serviceTime = atoi(argv[4]);
info.sampleInterval = atoi(argv[5]);
//Initiates Q to 0
for (int i = 0; i < maxCust; i++)
    info.Q[i] = 0;

You allocate the right amount of space in Q but you don't ever use that anywhere. Instead, you use info , which does not have any space for info.Q allocated. Therefore, info.Q[i] = 0 writes past the bounds of info . Suggested fix:

struct shiftInfo *info = malloc(sizeof(struct shiftInfo) + (maxCust*sizeof(int)));
info->simTime = atoi(argv[1]);
info->arrivalRate = atof(argv[2]);
info->tellers = atoi(argv[3]);
info->serviceTime = atoi(argv[4]);
info->sampleInterval = atoi(argv[5]);
//Initiates Q to 0
for (int i = 0; i < maxCust; i++)
    info->Q[i] = 0;

Next mistake was this:

status = pthread_create(&time, NULL, timer, (void *)info);

In your code, info was a struct. But you are trying to cast it to a pointer. If you use my suggested fix above, info will be a pointer and you can leave this code as is. You will need to make sure all your calls to pthread_create pass (void *)info and not (void *)&info .

The last mistake is here:

void *timer(void *info){
    int timeRemaining = info -> simTime;

Try this instead:

void *timer(void *arg){
    struct shiftInfo *info = (struct shiftInfo *) arg;
    int timeRemaining = info -> simTime;

This fragment is problematic:

struct shiftInfo *Q = malloc(sizeof(struct shiftInfo) + (maxCust*sizeof(int)));
struct shiftInfo info;
info.simTime = atoi(argv[1]);
info.arrivalRate = atof(argv[2]);
info.tellers = atoi(argv[3]);
info.serviceTime = atoi(argv[4]);
info.sampleInterval = atoi(argv[5]);
//Initiates Q to 0
for (int i = 0; i < maxCust; i++)
    info.Q[i] = 0;

The memory allocation is fine, but everything else is dubious indeed. Your local variable info has zero elements in the Q member, so you can't initialize it reliably with the for loop. It also isn't clear what you plan to do with info ; why aren't you initializing Q (the pointer — a badly chosen name, if I may say so). You should be using:

struct shiftInfo *shift = malloc(sizeof(struct shiftInfo) + (maxCust*sizeof(int)));
shift->simTime = atoi(argv[1]);
shift->arrivalRate = atof(argv[2]);
shift->tellers = atoi(argv[3]);
shift->serviceTime = atoi(argv[4]);
shift->sampleInterval = atoi(argv[5]);
for (int i = 0; i < maxCust; i++)
    shift->Q[i] = 0;

Now you can use shift (or whatever you choose to call it), setting a global pointer. Note that you cannot usefully have structures with flexible array members as global variables (global pointers to them — yes; global variables — no), nor can you have them as automatic variables. The trouble is that you cannot allocate any space for the flexible array member like that.

You will need to consider how you are going to control access to the global structure with multiple threads, and whether the serialized access to the global structure is going to become a serious bottleneck. You will likely need a mutex and maybe some conditions.

As to your timer code: you need to convert the void * that is passed into the struct ShiftInfo * .

void *timer(void *vp)
{
    struct ShiftInfo *info = vp;
    int timeRemaining = info->simTime;
    while(timeRemaining){
        sleep(1);
        timeRemaining--;
        }
    return 0;
}

Note that you do need to return a value from the function. Also, you need to consider whether this is going to do what you want. It has a local variable that is changed. You probably want to set the value in info to the time remaining. And that needs mutex access around every operation. I'm going to assume you add and initialize an extra member:

pthread_t mutex;

to your struct shifInfo .

void *timer(void *vp)
{
    struct shiftInfo *info = vp;
    if (pthread_mutex_lock(&info->mutex) != 0)
        return 0;  // Report error before returning?
    int *timeRemaining = &info->simTime;
    while (*timeRemaining > 0)
    {
        if (pthread_mutex_unlock(&info->mutex) != 0)
            return 0;  // Report error before returning?
        sleep(1);
        if (pthread_mutex_lock(&info->mutex) != 0)
            return 0;  // Report error before returning?
        (*timeRemaining)--;
    }
    if (pthread_mutex_unlock(&info->mutex) != 0)
        return 0;  // Report error before returning?
    return 0;
}

In timer() function, you have to cast info argument from (void *) to (struct shiftInfo *)

int timeRemaining = ((struct shiftInfo *)info) -> simTime;

Also, when creating customer, it should be like this

status = pthread_create(&customer, NULL, customer, (void *)&info);

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