C Programming

Pthread_Create() Function in C Language

Conceptually, a program is a single thread that executes several serial tasks, one after the other.
The C language allows us to write multithreaded programs using the pthread library of the POSIX standard.
A multi-threaded program is a thread or task that runs in parallel and concurrently with the main program and other threads opened by the same program in some part of it.

In this Linux Hint article, you will learn how to create a thread from a main program using the pthread_create() function of the pthread library.

This explains the theoretical workings of this function, its syntax, input and output arguments, and the data type accepted in each case.
We will then apply what we have learned in practical examples, including code snippets and images, in which we create and synchronize threads from the main function.

Syntax of the pthread_create() Function in C Language

int pthread_create(pthread_t *restrict thread,
                   const pthread_attr_t *restrict attr,
                   void *(*start_routine)(void *),
                   void *restrict arg);

Description of the pthread_create() Function in C Language

The pthread_create() function creates a thread and executes it, in parallel and concurrently with the program that created it. This function executes the routine specified by its pointer in the input start_routine by passing it the input argument arg.

Next, let us look at the input and output arguments of pthread_create() in detail, as well as a description of the work each of these arguments performs within the function.

thread: This is an output argument that returns the identifier of the created thread. This identifier is used to reference it in certain thread management functions such as pthread_join() or pthread_cancel().

attr: This entry is the pointer to a structure of type pthread_attr_t whose members specify the attributes of the new thread. When this argument is sent to NULL, the attributes of the new thread are taken with their default parameters.

start_routine: This is the pointer to the function that will be executed by the new thread.

arg: This is the pointer to the argument that the main function passes to the new thread.

If the thread is created successfully, pthread_create() returns 0 as the result. If an error occurs, it returns -1 and stores in the global variable errno the specific numeric value representing that error.

The pthread_create() function is defined in the pthread.h header. To use it, the following headers must be included in the “.c” file, as shown below:

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

Compilation Errors in Programs with Threads

When compiling GCC programs that use threads, compilation may fail if not done correctly from the command line.
The most common error message issued by the compiler states that one of the thread functions we refer to in the code is not defined.

These errors often result in wasting valuable time checking the headers we have inserted in the code, their integrity, and the directories associated with the compiler, since everything indicates that the problem is there.
Although the functions that cause the error are defined in the “pthread.h” header and included in the code, the compiler does not recognize these functions unless the pthread library is called from the command line during compilation.

Below, you can see in green the correct way to call the pthread library from the command console during compilation of programs with threads:

~$ gcc -pthread path/filename.c -o out_name

As shown in the figure below, the error disappears when the pthread library is called during compilation.

How to Create and Execute a Thread with the pthread_create() Function in the C Language

In this example, we will explain how pthread_create() works. To do this, we will create a main function and from there open a thread that executes the thread_function() function in parallel with the main() function.

The code for this example consists of two sections, the main() function and the thread_function() function, which is the thread.
Next, we will explain each of the two functions separately and then put them together to compile and run the code.

thread_function(): This function consists of a for loop with 5 cycles, in each of which the message “From the thread” is displayed in the command console and after a delay of 3 seconds the cycle is repeated again. The message of this function is interleaved with that of the main() function so that you can see in real time what each of the processes is doing simultaneously.

Next, we ncl see the nclusi the thread_function() function and the definition of its prototype:

void* thread_function(void* n);

void* thread_function(void* n)
{
  for (int a=0;a!=5; a++)
      {
       printf(“From the thread \n”);
       sleep(3);
      }
  printf(“nclus thread \n”);
  pthread_exit(n);

}

main() function: In this example, the main() function is nclusióne for defining the variables and creating the thread.

The first step in creating a thread is to define a variable of type pthread_t that ncl serve as the identifier of the thread. In this example we ncl call this variable thread_1.

To nclus the thread, we call the pthread_create() function and pass the thread identifier thread_1 as the first argument.
The attributes of the thread to be created are preset in this case, so the second input argument is NULL.
As the third argument, we pass the pointer to the function to be executed in the new thread, in this case thread_function().
Since we do not need to pass any arguments to the new thread in this example, the arg pointer ncl also be NULL.

After calling pthread_create(), the new thread begins execution and the function main() enters a for loop of 5 cycles, each of which prints the message “From main function” nested with the message “From the thread” of the function thread_function(). After each message, a delay of 3 seconds is inserted into the for loops of both functions before a new cycle is started.

Below you can see the nclusi the function main(), the nclusión of the necessary headers, and the declaration of the prototype of the function thread_function():

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

void* thread_function(void* data);

int main()
{
  pthread_t thread_1;
  printf("Creating the thread... \n");
  pthread_create (&thread_1,NULL,thread_function,NULL);
  for (int a=0;a!=5;a++)
    {
      printf("From main function \n");
    sleep(3);
    }
  sleep(3);
  return 0;
}

Next, we see the full code for this example. Copy and paste this fragment into a file with a “.c” extension. Compile the code and run it to see how the main() function and the new thread perform their tasks simultaneously.

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

void* thread_function(void* data);

int main()
{
  pthread_t thread_1;
  printf("Creating the thread... \n");
  pthread_create (&thread_1,NULL,thread_function,NULL);
  for (int a=0;a!=5;a++)
    {
      printf("From main function \n");
    sleep(3);
    }

  return 0;
}

/*****************************************************************/

void* thread_function(void* n)
{  
  for (int a=0;a!=5; a++)
      {
       printf("From the thread \n");
       sleep(3);
      }
  printf("End of thread \n");
  pthread_exit(n);

}

The image we see below shows the task executed by the thread we created with the pthread_create() function in parallel with the task of the main() function, and the messages each of them sends to the command console:

Synchronization of Threads in the C Language

In the code of the previous example, the execution time of the thread is shorter than that of the main() function and therefore both do their job correctly. This is due to the delay of 3 seconds that occurs when the main() function exits the loop.
However, if the execution times of a thread are longer than those of the main() function and it is fully executed, the program ends and all threads that were created and are still performing a task are automatically closed.

Let us see what happens in the code of the previous example if we set a delay of 1 second per cycle in the for loop of the main() function and one of 3 seconds in the thread loop.

As shown in the picture, the main() function completed the 5 cycles of its for loop, while the thread could only execute 3 cycles before the program closed.

Closing a thread without fully executing it can lead to critical problems in certain cases, as it could be storing user-generated data, writing to a file system or storage device, or performing some other task at the time of termination.
To avoid these problems, it is important to develop a mechanism to “wait” for the thread to complete execution before closing the program or executing a task. The pthread_join() function stops the function that created the thread until that thread finishes its execution.

The pthread_join() function takes two input arguments. The first is the thread identifier returned by the pthread_create() function when the thread is created and the second argument is the pointer to a variable that returns the exit status of the thread.

Next, we use the code from the previous example, but replace the for loop of the main() function with the pthread_join() function, as shown below:

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

void* thread_function(void* data);

int main()
{
  pthread_t thread_1;
  printf("Creating the thread... \n");
  pthread_create (&thread_1,NULL,thread_function,NULL);
 
  pthread_join (thread_1, NULL);

  return 0;
}

/*****************************************************************/

void* thread_function(void* n)
{  
  for (int a=0;a!=5; a++)
      {
       printf("From the thread \n");
       sleep(3);
      }
  printf("End of thread \n");
  pthread_exit(n);

}

In this case, the main() function will just create the thread and wait for it to finish its task, then close the program.

Possible Errors that the pthread_create() Function Can Generate and How to Recognize Them

The pthread_create() function can generate various errors, from invalid attribute settings to insufficient system resources for the new thread.
When a function generates an error, a numeric error identification code is stored in the global variable errno. This variable and the numeric definitions for each error are defined in the “errno.h” header.
Below is a description of each error that can occur when calling the pthread_create() function and its numeric representation defined in “errno.h”.

EAGAIN: There are no resources available to create another thread. The numeric representation of this error is 35.

EINVAL: The attribute configuration in attr is not valid. The numeric representation of this error is 22.

EPERM: Operation not allowed. This error occurs when you do not have sufficient permissions to set the attribute parameters in attr. The numerical representation of this error is 1.

Conclusion

In this Linux Hint article, we showed you how to use the pthread_create() function to create multitasking programs with threads that run in parallel with the main function.

We also told you how to compile programs that use threads correctly from the command line.
We have also included a special section explaining the importance of taking into account the execution times of the created threads and we have taught you how to synchronize the threads with the main function to achieve their correct execution.

About the author

Julio Cesar

Julio Cesar is a 42 years old programmer with 8 years of experience in embedded systems development, 6 years developing firmware for user interfaces in C and C++. Additionally he has 2 years of experience developing scripts for network devices and 3 years as developer of high frequency PCB (Printed Circuit Board).