A thread is defined as an entity of execution within a process; it is composed of a context and a sequence of instructions that will be executed. The thread will execute a procedure or function in the same process, concurrently with other threads. The context of the process and its data is common for all threads created by it. In memory, a thread will only reserve the space required for its stack.


Threads exist within a process. It is composed of a context with specific attributes, a user defined structure, a stack, a private data area, and a set of instructions that will be executed.

To work with threads, as a user, you need to:
- Creating a thread;
- Configure the thread;
- Scheduling;
- Execute the thread;
- Cooperation with other threads of the program and / or system through
timing elements;
- Ending a thread activity.

 -------------------------------------------------- ---------------
    Posix Threads
For Posix threads you need to use the header file <pthread.h>.

When compiling programs that use POSIX threads, you need to indicate this to the compiler. You do this with the option -lpthread in your compile command.

1. Create a thread:
    int pthread_create (pthread_t * tid, pthread_attr_t * attr,
                void * (* function) (void *), void * arg);
where:
- tid is the thread descriptor. The pthread_create function returns the value 0 on success and a non-zero value (error code), in case of failure;
- the execution of the new thread is described by function whose name is stated by the "function" parameter;
- attr parameter allows you to specify some specific attributes of a thread. If attr is NULL, the system will set the default attribute values.

2. Ending a thread may occur in the following situations:
- When the function that describes the thread ends.
- You can end a thread by calling the function
    pthread_exit int (int * status).

3. Waiting for a thread to finish its execution
    int pthread_join (tid pthread_t, void ** status);
phread_join suspends the execution of the calling thread until the thread with the specified tid descriptor terminates. For one thread, you can call this function only once.

 

------------------------------------------------------------------------

Some examples
ex1. How to create a thread

p1thread.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_TH 5

void *PrintM (void *threadid)
{
  long tid;
  tid = (long) threadid;
  printf ("Salut! Aici thread-ul #%ld!\n", tid);
  pthread_exit (NULL);
}

int main (int argc, char *argv[])
{
  pthread_t threads[NUM_TH];
  int rc;
  long t;
  for (t = 0; t < NUM_TH; t++)
    {
      printf ("In main: create thread %ld\n", t);
      rc = pthread_create (&threads[t], NULL, PrintM, (void *) t);
      if (rc)
    {
      printf ("ERROR; the error code for pthread_create() is %d\n",
          rc);
      exit (-1);
    }
    }
  pthread_exit (NULL);
}

compiling and running the code:
$gcc -pthread -Wall p1thread.c -o p1thread.o
$./p1thread


-------------------------------------------------------------------------
ex2. Transmitting some parameters to a thread.

thrPassArg1.c:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_TH 4

void *PrintM (void *threadArg)
{
  int *tid;
  tid = (int *) threadArg;
  //sleep (1);
  printf ("Function PrintM executed by the thread %d.\n", *tid);
  pthread_exit (NULL);
}

int main (int argc, char *argv[])
{
  pthread_t threads[NUM_TH];
  int *tids[NUM_TH];//thread id
  int t, rc;

  for (t = 1; t < NUM_TH + 1; t++)
    {
      tids[t] = (int *) malloc(sizeof(int));
      *tids[t] = t;
      printf ("Create the thread %d\n", t);
      rc = pthread_create (&threads[t], NULL, PrintM, (void *)
                           tids[t]);
      if (rc)
        {
          printf ("ERROR; the error code for the pthread_create() function - %d\n", rc);
          exit (-1);
        }
    }
  pthread_exit (NULL);
}

compiling and running the code:
$gcc -Wall -pthread thrPassArg1.c -o thrPassArg1.o
$./thrPassArg1.o


-------------------------------
Using a structure to transmit parameters.
thrPassArg2.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUM_TH 4

struct thread_data
{
  int thread_id;
  int n1;
  int n2;
};

struct thread_data thread_data_array[NUM_TH];

void *PrintM (void *threadArg)
{
  int taskid, sum, n1, n2;
  struct thread_data *my_data;

  sleep (1);
  my_data = (struct thread_data *) threadArg;
  taskid = my_data->thread_id;
  n1 = my_data->n1;
  n2 = my_data->n2;
  sum = n1 + n2;
  printf ("Thread %d, %d + %d = %d\n", taskid, n1, n2, sum);
  pthread_exit (NULL);
}

int main (int argc, char *argv[])
{
  pthread_t threads[NUM_TH];
  int rc, t, n1, n2;

  for (t = 1; t < NUM_TH + 1; t++)
    {
      n1 = rand () % 100;
      n2 = rand () % 100 + 100;
      thread_data_array[t].thread_id = t;
      thread_data_array[t].n1 = n1;
      thread_data_array[t].n2 = n2;
      printf ("Creez thread-ul %d\n", t);
      rc = pthread_create (&threads[t], NULL, PrintM, (void *)
                           &thread_data_array[t]);
      if (rc)
        {
          printf ("ERROR; the error code returned by the function pthread_create() - %d\n", rc);
          exit (-1);
        }
    }
  pthread_exit (NULL);
}

compiling and running the code:
$gcc -Wall -pthread thrPassArg2.c -o thrPassArg2.o
$./thrPassArg2.o


-------------------------------------------------------------------------
ex3. Thread synchronization with pthread_join()

p3thread.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

#define NUM_TH 4

void *BusyWork (void *t)
{
  int i;
  long tid;
  double res = 0.0;
  tid = (long) t;
  printf ("Start thread %ld...\n", tid);
  for (i = 0; i < 1000000; i++)
    {
      res += sin(i) * tan(i);
    }
  //sleep(2);
  printf ("End thread %ld. The result is %e\n", tid, res);
  pthread_exit ((void *) t);
}

int main (int argc, char *argv[])
{
  pthread_t thread[NUM_TH];
  pthread_attr_t attr;
  int rc;
  long t;
  void *status;

  pthread_attr_init (&attr);
  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);

  for (t = 0; t < NUM_TH; t++)
    {
      printf ("Main process: create the thread %ld\n", t);
      rc = pthread_create (&thread[t], &attr, BusyWork, (void *) t);
      if (rc)
    {
      printf ("ERROR; %d\n", rc);
      exit (-1);
    }
      //sleep(2);
    }

  //free resources and wait for the other threads to finish
  pthread_attr_destroy (&attr);
  for (t = 0; t < NUM_TH; t++)
    {
      rc = pthread_join (thread[t], &status);
      if (rc)
    {
      printf ("ERROR; %d\n", rc);
      exit (-1);
    }
      printf("Main process: finished join with thread %ld, the status is %ld\n", t, (long) status);
      //sleep(1);
    }

  printf ("Finish!\n");
  pthread_exit (NULL);
}

compiling and running the code:
$gcc -pthread -Wall p3thread.c -o p3thread.o -lm
$./p3thread.o

----------------------------------------------------------------------
Problems
1. Solve one problem from lab 5-6 using threads.

2. Write a C program that receives as arguments file names and processes them simultaneously using threads. The program transforms the files so that all words will begin with a capital letter. The modified files (containing the first letter words capitalized) will have the same name as the original files and will end woth the number N (N is the thread id). You will create a thread for each file.