简体   繁体   中英

Issue with POSIX thread synchronization and/or pthread_create() argument passing

I am trying to create a number of different threads that are required to wait for all of the threads to be created before they can perform any actions. This is a smaller part of a large program, I am just trying to take it in steps. As each thread is created it is immediately blocked by a semaphore. After all of the threads have been created, I loop through and release all the threads. I then wish each thread to print out its thread number to verify that they all waited. I only allow one thread to print at a time using another semaphore.

The issue I'm having is that although I create threads #1-10, a thread prints that it is #11. Also, a few threads say they have the same number as another one. Is the error in my passing the threadID or is it in my synchronization somehow?

Here is relevant code:

//Initialize semaphore to 0. Then each time a thread is spawned it will call
    //semWait() making the value negative and blocking that thread. Once all of the
    //threads are created, semSignal() will be called to release each of the threads
     sem_init(&threadCreation,0,0);


     sem_init(&tester,0,1);


    //Spawn all of the opener threads, 1 for each valve
    pthread_t threads[T_Valve_Numbers];
    int check;

    //Loop starts at 1 instead of the standard 0 so that numbering of valves
    //is somewhat more logical. 
    for(int i =1; i <= T_Valve_Numbers;i++)
    {
        cout<<"Creating thread: "<<i<<endl;
        check=pthread_create(&threads[i], NULL, Valve_Handler,(void*)&i);
        if(check)
        {
            cout <<"Couldn't create thread "<<i<<" Error: "<<check<<endl;
            exit(-1);
        }
    }

    //Release all of the blocked threads now that they have all been created
    for(int i =1; i<=T_Valve_Numbers;i++)
    {
        sem_post(&threadCreation);
    }

    //Make the main process wait for all the threads before terminating
    for(int i =1; i<=T_Valve_Numbers;i++)
    {
        pthread_join(threads[i],NULL);
    }
    return 0;



}   
    void* Valve_Handler(void* threadNumArg)
    {   
        int threadNum = *((int *)threadNumArg);
        sem_wait(&threadCreation);//Blocks the thread until all are spawned

        sem_wait(&tester);
        cout<<"I'm thread "<<threadNum<<endl;
        sem_post(&tester);
    }

When T_Valve_Numbers = 10, some sample output is:

Creating thread: 1
Creating thread: 2
Creating thread: 3
Creating thread: 4
Creating thread: 5
Creating thread: 6
Creating thread: 7
Creating thread: 8
Creating thread: 9
Creating thread: 10
I'm thread 11   //Where is 11 coming from?
I'm thread 8
I'm thread 3
I'm thread 4
I'm thread 10
I'm thread 9
I'm thread 7
I'm thread 3
I'm thread 6
I'm thread 6   //How do I have 2 6's?

OR

Creating thread: 1
Creating thread: 2
Creating thread: 3
Creating thread: 4
Creating thread: 5
Creating thread: 6
Creating thread: 7
Creating thread: 8
Creating thread: 9
Creating thread: 10
I'm thread 11
I'm thread 8
I'm thread 8
I'm thread 4
I'm thread 4
I'm thread 8
I'm thread 10
I'm thread 3
I'm thread 9
I'm thread 8 //Now '8' showed up 3 times

"I'm thread..." is printing 10 times so it appears like my semaphore is letting all of the threads through. I'm just not sure why their thread number is messed up.

check=pthread_create(&threads[i], NULL, Valve_Handler,(void*)&i);
                                                             ^^

You're passing the thread start function the address of i . i is changing all the time in the main loop, unsynchronized with the thread functions. You have no idea what the value of i will be once the thread function gets around to actually dereferencing that pointer.

Pass in an actual integer rather than a pointer to the local variable if that's the only thing you'll ever need to pass. Otherwise, create a simple struct with all the parameters, build an array of those (one for each thread) and pass each thread a pointer to its own element.

Example: (assuming your thread index never overflows an int )

#include <stdint.h> // for intptr_t

...
check = pthread_create(..., (void*)(intptr_t)i);
...

int threadNum = (intptr_t)threadNumArg;

Better/more flexible/doesn't require intprt_t that might not exist example:

struct thread_args {
  int thread_index;
  int thread_color;
  // ...
}

// ...

struct thread_args args[T_Valve_Numbers];
for (int i=0; i<T_Valve_Numbers; i++) {
  args[i].thread_index = i;
  args[i].thread_color = ...;
}

// ...

check = pthread_create(..., &(args[i-1])); // or loop from 0, less surprising

A word of caution about this though: that array of thread arguments needs to stay alive at least as long as the threads will use it. In some situations, you might be better of with a dynamic allocation for each structure, passing that pointer (and its ownership) to the thread function (especially if you're going to detach the threads rather than joining them).

If you're going to join the threads at some point, keep those arguments around the same way you keep your pthread_t structures around. (And if you're creating and joining in the same function, the stack is usually fine for that.)

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