C++ multithreading


Multi-threading is a special form of multitasking, which allows the computer to run two or more programs at the same time. In general, there are two types of multitasking: process-based and thread-based.

  • Process-based multitasking is the concurrent execution of programs.

  • Thread-based multitasking is the concurrent execution of fragments of the same program.

A multithreaded program consists of two or more parts that can run simultaneously. Each part of such a program is called a thread, and each thread defines a separate execution path.

C++ does not contain any built-in support for multi-threaded applications. Instead, it relies entirely on the operating system to provide this functionality.

This tutorial assumes that you are using a Linux operating system and we are going to use POSIX to write a multi-threaded C++ program. POSIX Threads or Pthreads provide an API available on a variety of Unix-like POSIX systems, such as FreeBSD, NetBSD, GNU/Linux, Mac OS X, and Solaris.

Create thread

The following program, we can use it to create a POSIX thread:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

Here, pthread_create Create a new thread , and make it executable. The following is a description of the parameters:

ParameterDescription
threadpoints to Thread identifier pointer.
attrAn opaque attribute object that can be used to set thread attributes. You can specify a thread properties object, or use the default value of NULL.
start_routineThe starting address of the thread running function will be executed once the thread is created.
arg Parameters to run the function. It must be passed by casting the reference as a pointer to void type. If no parameters are passed, NULL is used.

When the thread is successfully created, the function returns 0. If the return value is not 0, the thread creation fails.

Terminating threads

Using the following program, we can use it to terminate a POSIX thread:

#include <pthread.h>
pthread_exit (status)

Here, pthread_exit is used explicitly Exit a thread. Normally, the pthread_exit() function is called when the thread has completed its work and no longer needs to continue to exist.

If main() ends before the thread it created and exits through pthread_exit(), then other threads will continue to execute. Otherwise, they will be automatically terminated at the end of main().

Example

The following simple example code uses the pthread_create() function to create 5 threads, each thread outputs "Hello php!":

#include <iostream>
// 必须的头文件是
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

// 线程的运行函数
void* say_hello(void* args)
{
    cout << "Hello php!" << endl;
}

int main()
{
    // 定义线程的 id 变量,多个变量使用数组
    pthread_t tids[NUM_THREADS];
    for(int i = 0; i < NUM_THREADS; ++i)
    {
        //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
        int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
        if (ret != 0)
        {
           cout << "pthread_create error: error_code=" << ret << endl;
        }
    }
    //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
    pthread_exit(NULL);
}

Use -lpthread library Compile the following program:

$ g++ test.cpp -lpthread -o test.o

Now, execute the program and the following results will be produced:

$ ./test.o
Hello php!
Hello php!
Hello php!
Hello php!
Hello php!

The following simple example code uses the pthread_create() function to create 5 threads and receive the incoming parameter. Each thread prints a "Hello php!" message and outputs the received parameters, and then calls pthread_exit() to terminate the thread.

//文件名:test.cpp

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS     5

void *PrintHello(void *threadid)
{  
   // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
   int tid = *((int*)threadid);
   cout << "Hello php! 线程 ID, " << tid << endl;
   pthread_exit(NULL);
}

int main ()
{
   pthread_t threads[NUM_THREADS];
   int indexes[NUM_THREADS];// 用数组来保存i的值
   int rc;
   int i;
   for( i=0; i < NUM_THREADS; i++ ){      
      cout << "main() : 创建线程, " << i << endl;
      indexes[i] = i; //先保存i的值
      // 传入的时候必须强制转换为void* 类型,即无类型指针        
      rc = pthread_create(&threads[i], NULL, 
                          PrintHello, (void *)&(indexes[i]));
      if (rc){
         cout << "Error:无法创建线程," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Now compile and execute the program, which will produce the following results:

$ g++ test.cpp -lpthread -o test.o
$ ./test.o
main() : 创建线程, 0
main() : 创建线程, 1
main() : 创建线程, 2
main() : 创建线程, 3
main() : 创建线程, 4
Hello php! 线程 ID, 4
Hello php! 线程 ID, 3
Hello php! 线程 ID, 2
Hello php! 线程 ID, 1
Hello php! 线程 ID, 0

Passing parameters to the thread

This example demonstrates how to pass multiple parameters through a structure. You can pass any data type in the thread callback since it points to void, as shown in the following example:

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS     5

struct thread_data{
   int  thread_id;
   char *message;
};

void *PrintHello(void *threadarg)
{
   struct thread_data *my_data;

   my_data = (struct thread_data *) threadarg;

   cout << "Thread ID : " << my_data->thread_id ;
   cout << " Message : " << my_data->message << endl;

   pthread_exit(NULL);
}

int main ()
{
   pthread_t threads[NUM_THREADS];
   struct thread_data td[NUM_THREADS];
   int rc;
   int i;

   for( i=0; i < NUM_THREADS; i++ ){
      cout <<"main() : creating thread, " << i << endl;
      td[i].thread_id = i;
      td[i].message = "This is message";
      rc = pthread_create(&threads[i], NULL,
                          PrintHello, (void *)&td[i]);
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

When the above code is compiled and executed, it produces the following results:

$ g++ -Wno-write-strings test.cpp -lpthread -o test.o
$ ./test.o
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message

Connecting and detaching threads

We can use the following two functions to connect or detach threads:

pthread_join (threadid, status) 
pthread_detach (threadid)

pthread_join() subroutine blocks the calling program until the specified threadid thread terminates . When a thread is created, one of its properties defines whether it is joinable or detached. Only threads defined as connectable when created can be connected. If a thread is defined as detachable when it is created, it can never be connected.

This example demonstrates how to use the pthread_join() function to wait for the thread to complete.

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

using namespace std;

#define NUM_THREADS     5

void *wait(void *t)
{
   int i;
   long tid;

   tid = (long)t;

   sleep(1);
   cout << "Sleeping in thread " << endl;
   cout << "Thread with id : " << tid << "  ...exiting " << endl;
   pthread_exit(NULL);
}

int main ()
{
   int rc;
   int i;
   pthread_t threads[NUM_THREADS];
   pthread_attr_t attr;
   void *status;

   // 初始化并设置线程为可连接的(joinable)
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

   for( i=0; i < NUM_THREADS; i++ ){
      cout << "main() : creating thread, " << i << endl;
      rc = pthread_create(&threads[i], NULL, wait, (void *)i );
      if (rc){
         cout << "Error:unable to create thread," << rc << endl;
         exit(-1);
      }
   }

   // 删除属性,并等待其他线程
   pthread_attr_destroy(&attr);
   for( i=0; i < NUM_THREADS; i++ ){
      rc = pthread_join(threads[i], &status);
      if (rc){
         cout << "Error:unable to join," << rc << endl;
         exit(-1);
      }
      cout << "Main: completed thread id :" << i ;
      cout << "  exiting with status :" << status << endl;
   }

   cout << "Main: program exiting." << endl;
   pthread_exit(NULL);
}

When the above code is compiled and executed, it produces the following results:

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread 
Thread with id : 4  ...exiting 
Sleeping in thread 
Thread with id : 3  ...exiting 
Sleeping in thread 
Thread with id : 2  ...exiting 
Sleeping in thread 
Thread with id : 1  ...exiting 
Sleeping in thread 
Thread with id : 0  ...exiting 
Main: completed thread id :0  exiting with status :0
Main: completed thread id :1  exiting with status :0
Main: completed thread id :2  exiting with status :0
Main: completed thread id :3  exiting with status :0
Main: completed thread id :4  exiting with status :0
Main: program exiting.