How to Create Threads in Linux

In this article, we’ll focus on how to create and identify threads. We’ll also provide an example of a working C program that explains how to do basic thread programming.

thread identification

Just as processes are identified by process ID, threads are identified by thread ID. But interestingly, the similarities between the two end there.

  • Process IDs are unique across the system, while thread IDs are unique only within the context of a single process.
  • Process IDs are integer values, but thread IDs are not necessarily integer values. it is most likely a struct
  • Process IDs can be easily printed, while thread IDs are not.

The above points give an idea about the difference between process ID and thread ID.

The thread ID is represented by the type “pthread_t”. As we’ve already discussed, in most cases this type is a struct, so there must be a function that can compare two thread IDs.

#include <pthread.h>
int pthread_equal(pthread_t tid1,pthread_t tid2);

As you can see, the above function takes two thread IDs and returns a non-zero value if the two thread IDs are equal, and zero otherwise.

Another situation may arise when a thread wants to know its own thread ID. For this case, the following function provides the required service.

#include <pthread.h>
pthread_t pthread_self(void);

So we see that the function ‘pthread_self()’ is used by a thread to print its own thread ID.

Now, one would ask the situation where the above two functions are required. Suppose there is a case where a linked list contains data from different threads. Each node in the list contains a thread ID and corresponding data. Now whenever a thread tries to get its data from the linked list, it first gets its own ID by calling ‘pthread_self()’, then it calls ‘pthread_equal()’ on each node to see if the node contains its data.

An example of the general case discussed above is where the main thread gets jobs to process and then pushes them into a linked list. Now the individual worker threads parse the linked list and extract the jobs assigned to them.

thread creation

Normally, when a program starts and becomes a process, it starts with the default thread. So we can say that every process has at least one thread of control. A process can create additional threads using the following functions:

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg)

The above function takes four arguments, let’s discuss them first:

  • The first parameter is an address of type pthread_t. Once the function call is successful, the variable whose address is passed as the first parameter will hold the thread ID of the newly created thread.
  • The second parameter may contain some properties we want the new thread to contain. It could be priority etc.
  • The third parameter is the function pointer. The thing to remember is that each thread starts with a function and the function address is passed here as the third parameter so that the kernel knows which function to start the thread from.
  • Since the function (whose address is passed in the third parameter above) can also accept some parameters, we can pass these parameters as pointers to void type. Now, why choose the void type? This is because if a function accepts multiple parameters, then the pointer may be a pointer to a structure that may contain those parameters.

A practical thread example

Below is sample code where we try to use all three functions discussed above.

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

pthread_t tid[2];

void* doSomeThing(void *arg)
{
    unsigned long i = 0;
    pthread_t id = pthread_self();

    if(pthread_equal(id,tid[0]))
    {
        printf("\n First thread processing\n");
    }
    else
    {
        printf("\n Second thread processing\n");
    }

    for(i=0; i<(0xFFFFFFFF);i++);

    return NULL;
}

int main(void)
{
    int i = 0;
    int err;

    while(i < 2)
    {
        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
        if (err != 0)
            printf("\ncan't create thread :[%s]", strerror(err));
        else
            printf("\n Thread created successfully\n");

        i++;
    }

    sleep(5);
    return 0;
}

So what this code does is:

  • It creates two threads using pthread_create() function
  • The start function of both threads remains the same.
  • Inside the function ‘doSomeThing()’, the thread uses the pthread_self() and pthread_equal() functions to identify whether the executing thread is the first thread or the second thread when it was created.
  • Also, run a for loop in the same function ‘doSomeThing()’ to simulate some time consuming work.

Now, when the above code runs, the output is as follows:

$ ./threads
Thread created successfully
First thread processing
Thread created successfully
Second thread processing

As the output shows, create the first thread and start processing, then create the second thread and start processing. One thing to note here is that the execution order of threads is not always fixed. It depends on the scheduling algorithm of the operating system.

Note: All explanations in this article are done on Posix threads. As can be understood from the type, the pthread_t type represents a POSIX thread. If an application wants to test whether POSIX threads are supported, the application can use the macro _POSIX_THREADS for compile-time testing. To compile code that contains calls to the posix API, use the compile option “-pthread”.

Read More:

Leave a Reply

Your email address will not be published. Required fields are marked *