clinuxpthreadsposixpthread-join

C: Printing out multiple threads' ID's before they execute anything?


I am wanting to generate a number of threads based off of how many a user wishes to create, and each of these threads is to carry out one function "readFiles". However, before the threads can each execute any of the meaningful code from readFiles, (represented by the "..." 's), I want ALL of the threads to each print out their thread ID's in a sort of introductory notification message, so something like...

"Thread ID _____ is processing!"
"Thread ID _____ is processing!"
"Thread ID _____ is processing!"
    ...
    ...
    ...

...but obviously with the code I have, each thread is only running one at a time so the thread ID's are only printing out when each new thread starts executing meaningful code, so this...

"Thread ID _____ is processing!"
    ...
"Thread ID _____ is processing!"
    ...
"Thread ID _____ is processing!"
    ...

Is there a way to get thread ID's before I call pthread_create() on each specific new thread? Or is there a way in which I can make these threads (which are supposed to run concurrently), hold off or pause their execution of the meaningful code within readFiles (their "..." 's) until they have each printed out the thread ID messages first? Thank you everybody so much!!! This is my code to give you a better idea of what I have at the moment...

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

uint64_t gettid()
{
    pthread_t actual_tid = pthread_self();
    uint64_t threadId = 0;
    memcpy( &threadId, &actual_tid, sizeof( actual_tid ) );
    return threadId;
}


void *readFiles( void *args )
{
    pthread_mutex_lock(&mutex);


    uint64_t current_id = gettid();
    printf("Thread id = ");
    printf("%" PRIu64 , current_id );

    ...
    ...
    ...

    pthread_mutex_unlock(&mutex);
}


void alphabetcountmulthreads(int num_threads)
{

    pthread_t ids[num_threads];

    for ( int t = 0; t < num_threads; t++ )
    {
        
        if ( pthread_create( &ids[t], NULL, &readFiles, NULL ) != 0 )
        {
            fprintf( stderr, "error: Cannot create thread # %d\n", t );
            break;
        }
    }

    for ( int u = 0; u < num_threads; ++u )
    {
        if ( pthread_join( ids[u], NULL ) != 0 )
        {
            fprintf( stderr, "error: Cannot join thread # %d\N", u );
        }
    }    

}

Solution

  • One simple way to implement a thread pool with each thread having an opportunity to do preparatory work, before all threads will start actual work, is to have an additional startup mutex, associated condition variable, and a counter for ready threads:

    static pthread_mutex_t  prep_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_cond_t   prep_cond = PTHREAD_COND_INITIALIZER;
    static volatile size_t  prep_count = 0;
    
    static pthread_mutex_t  thread_mutex = PTHREAD_MUTEX_INITIALIZER;
    static volatile size_t  thread_count = 0;
    
    static inline pid_t gettid(void) { return syscall(SYS_gettid); }
    
    static void *thread_function(void *idref)
    {
        /* Get the number/index of this thread. */
        const size_t  num = *(pid_t *)idref;
        const pid_t   tid = gettid();
    
        /* Replace it with the Linux tid. */
        *(pid_t *)idref = tid;
    
        /* Block here until preparatory work can be done. */
        pthread_mutex_lock(&prep_mutex);
    
        printf("Thread %zu of %zu has started.\n", id + 1, thread_count);
        fflush(stdout);
    
        prep_count++;
        pthread_cond_signal(&prep_cond);
        pthread_mutex_unlock(&prep_mutex);
    
        /* Block until actual work can start. */
        pthread_mutex_lock(&thread_mutex);
        ...
    

    When creating the thread pool, the creator holds both mutexes. When it is ready to let the threads start the prep work, it releases the prep_mutex, waiting on the prep_cond until sufficient number of threads have completed preparatory work (prep_count is high enough). Then, it releases the thread_mutex.

    int start_threads(size_t max_count)
    {
        pthread_t       thread_id[max_count];
        pid_t           linux_tid[max_count];
        pthread_attr_t  attrs;
        int             err = 0;
    
        /* Assume no threads currently running */
        prep_count = thread_count = 0;
    
        /* Shrink stack size; the default is too large. */
        pthread_attr_init(&attrs);
        pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
    
        /* Grab the mutexes, so the threads will block initially. */
        pthread_mutex_lock(&prep_mutex);
        pthread_mutex_lock(&thread_mutex);
    
        while (thread_count < max_count) {
            linux_tid[thread_count] = thread_count;
            err = pthread_create(thread_id + thread_count, &attrs,
                                 thread_function, linux_tid + thread_count);
            if (err)
                break;
    
            thread_count++;
        }
    
        /* Attributes are no longer needed. */
        pthread_attr_destroy(&attrs);
    
        /* No threads created at all? */
        if (thread_count < 1) {
            pthread_mutex_unlock(&prep_mutex);
            pthread_mutex_unlock(&thread_mutex);
            /* Return failure. */
            return err;
        }
    
        /* All threads have now been created; let them do prep work. */
        while (prep_count < thread_count) {
            pthread_cond_wait(&prep_cond, &prep_mutex);
        }
        pthread_mutex_unlock(&prep_mutex);
    
        /* All threads have finished their prep work; start working. */
        pthread_mutex_unlock(&thread_mutex);
    
        ...
    

    The above creates up to max_num threads, with their pthread IDs in thread_id[], and their Linux tids in linux_tid[].

    When the thread starts, it gets a pointer to the location to store the Linux tid to. Initially, that contains the index of the thread ("thread number", starting at 0), so thread_function grabs that as num, obtains the Linux tid as tid, and stores it to the location given as a parameter. This way, both the original thread and the created thread know the index (num), pthread ID (thread_id[], pthread_self()), and Linux tid (linux_tid[] and tid).

    Note how the arrays are dereferenced in the pthread_create() call. If array is an array, and index is an index to it, then &(array[index]) == array + index.

    Above, the start_threads() function does not assume it can start all max_num threads. It does return with nonzero errno number if it cannot create any, but if it can create at least one thread, as soon as the threads can grab prep_mutex, the thread_count will reflect the correct number of threads in their gang. (There is no reason to fail just because you were only able to create 53 worker threads instead of 54, say.)