5

I'm writing a Unix application in C which uses multiple threads of control. I'm having a problem with the main function terminating before the thread it has spawned have a change to finish their work. How do I prevent this from happening. I suspect I need to use the pthread_join primitive, but I'm not sure how. Thanks!

gamma4332
  • 51
  • 1
  • 2

5 Answers5

8

Yes, you could use pthread_join() (see other anwers for how to do that). But let me explain the pthread model and show you another option.

In Unix, a process exits when the primary thread returns from main, when any thread calls exit() or when the last thread calls pthread_exit(). Based on the last option, you can simply have your main thread call pthread_exit() and the process will stay alive as long as at least one more thread is running.

R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
5

Yes one of doing this is to use pthread_join function: that's assuming your thread is in "joinable" state.

  • pthread_create: after this function returns control, your thread will be executing your thread function.

  • after pthread_create, use the tid from pthread_create to pthread__join.

If your thread is detached, you must use some other technique e.g. shared variable, waiting on signal(s), shared queue etc.

Great reference material available here.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
jldupont
  • 93,734
  • 56
  • 203
  • 318
  • 2
    drive-by down-votes without comments... don't you just love those... not! – jldupont Nov 08 '09 at 02:30
  • 1
    Shared variables, signals, or queues would be unnecessarily complicated. Just call pthread_exit() from main() and be done with it. – Dan Moulding Nov 08 '09 at 02:30
  • 2
    @dan: I guess it pretty much depends **what** your are trying to do in the first place, now doesn't it. It is **not** because that's the technique you've used on **some** occasions that it makes it **the tool**. If you down-voted me on that premise (but I am sure you haven't) then you might want to reconsider. – jldupont Nov 08 '09 at 02:35
  • 1
    @jldupont: For a given problem, I prefer a simple solution that solves that problem over an unnecessarily complex solution. It doesn't matter *what* it is that you are trying to do. Going with the simple solution is just plain good ol' engineering sense. There is virtually *never* a case where an unnecessarily complex solution is better than a simple one. pthread_exit will almost *always* be the simplest solution to the problem posed by the OP. I downvoted your answer because it *only* suggested other unnecessarily complicated solutions to the problem. Please don't take it personally. – Dan Moulding Nov 08 '09 at 04:21
  • 1
    @dan: I agree with the simplest solution but in the case of **detached** threads, you must have an information of some kind **before** you exit the thread. Granted that **if** the thread knows it can exit safely, you don't need the additional overhead. Anyhow, I am done arguing on this one. – jldupont Nov 08 '09 at 11:46
3

There are a number of different ways you can do this, but the simplest is to call pthread_exit() before returning from main().

Note that this technique works even if the thread you want to wait for is not joinable.

Dan Moulding
  • 211,373
  • 23
  • 97
  • 98
  • I don't know pthreads specifically, but in Unix I think it is a bad thing to exit the main thread with child threads still running since only the main thread can respond correctly to signals targeting the process. Note, I said Unix. My *nix training is nearly two decades old. (Oy.) – jmucchiello Nov 08 '09 at 02:38
  • In theory, all the threads can handle signals, and it is not deterministic which one actually does so. Whether what happens in theory also happens in practice is perhaps a bit more debatable. – Jonathan Leffler Nov 08 '09 at 02:59
  • @jmucchiello: From what I've been able to gather, pthreads were first specified in SUSv2 -- in 1997. So it sounds like your Unix training might pre-date pthreads, so what you worked with at that time might not bear any relation to modern Unix. In any case, I can tell you that my POSIX training, which is significantly less than two decades old, has taught me that signals can be caught without any problem after the main thread has exited ;) – Dan Moulding Nov 08 '09 at 04:51
  • @jmucchiello: Correction on the first publication of the pthreads spec: it goes back at *least* as far as 1995. Back then it was known as IEEE 1003.1c, and it obviously predates SUSv2. – Dan Moulding Nov 08 '09 at 05:13
  • +1. `pthread_exit` from the `main()` thread is an easy, but relatively unknown technique. (Most ref docs I've seen on `pthread_exit` tell the reader more about `exit(3)` in a pthreads context rather than this function.) – pilcrow Nov 25 '09 at 15:04
1

Check this simple C code I wrote for one of my libraries

/*
 *   Copyright (c) 2011 Dino Ciuffetti <dino@tuxweb.it>, TuxWeb S.r.l., NuvolaBase Ltd
 *   
 *   This file is part of liborient.
 *
 *   Liborient is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Liborient is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with Liborient.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//pthread_rwlock_t ptr_thr_lock = PTHREAD_RWLOCK_INITIALIZER;

typedef struct {
        int t;
} thread_arguments;

void *thread_stuff(void *args) {
        thread_arguments *t_args;
        int tid;

        t_args = (thread_arguments *)args;
        //pthread_rwlock_rdlock(&ptr_thr_lock);
        tid = t_args->t;
        //pthread_rwlock_unlock(&ptr_thr_lock);

        /*while (1) {
        sleep (1);*/
        printf("Thread #%i!\n", tid);
        /*}*/

        t_args = NULL;
        pthread_exit(NULL);
        return NULL;
}

int wait_threads(pthread_t threads[], thread_arguments *t_args[], int nthreads) {
        int t;
        int rc;

        // Waiting for threads termination
        for(t=0; t<nthreads; t++) {
                rc = pthread_join(threads[t], NULL);
                free(t_args[t]);
                if (rc != 0) {
                        printf("Error waiting for termination of thread %i: %i\n", t, rc);
                        return 1;
                        break;
                }
        }

        return 0;
}

int spawn_threads(pthread_t threads[], thread_arguments *t_args[], int nthreads) {
        int t;
        int rc;

        // Spawning threads
        for(t=0; t<nthreads; t++) {
                t_args[t] = (thread_arguments *) malloc(sizeof(thread_arguments));
                //pthread_rwlock_wrlock(&ptr_thr_lock);
                t_args[t]->t = t;
                //pthread_rwlock_unlock(&ptr_thr_lock);

                printf("Spawning thread: %i\n", t);
                rc = pthread_create(&threads[t], NULL, (void *)thread_stuff, (void *)t_args[t]);
                if (rc != 0) {
                        printf("Error spawning thread %i: %i\n", t, rc);
                        wait_threads(threads, t_args, rc+1);
                        return t+1;
                        break;
                }
        }

        return 0;
}

int main() {
        pthread_t threads[20];
        thread_arguments *t_args[20];
        int rc;

        rc = spawn_threads(threads, t_args, 20);
        if (rc > 0) {
                printf("Failed spawning thread number %i\n", rc-1);
                return 1;
        }

        rc = wait_threads(threads, t_args, 20);

        return 0;
}
dAm2K
  • 9,923
  • 5
  • 44
  • 47
1

You may want to look at this page: http://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/apis/users_25.htm

 rc = pthread_create(&thread, NULL, threadfunc, NULL);
  checkResults("pthread_create()\n", rc);

  printf("Wait for the thread to exit\n");
  rc = pthread_join(thread, &status);
James Black
  • 41,583
  • 10
  • 86
  • 166