Pthread是由POSIX提出的一套通用的线程库,在linux平台下,它被广泛的支持,而windows平台下,却并不被支持,而pthreads-w32为我们提供了解决方案

     多线程编程需要包含头文件   #include <pthread.h>

     #include <unistd.h> 中的函数 long sysconf (_SC_NPROCESSORS_ONLN); 可以获取当前CPU的核数。

     多线程程序也可以在单核CPU上运行,但同一时间只能一个线程在跑, 系统帮你切换线程而已, 系统给每个线程分配时间片来执行, 每个时间片大概10ms左右, 看起来像是同时跑, 但实际上是每个线程跑一点点就换到其它线程继续跑。

     多线程编程时,可以指定CPU完成某一个任务,也就是线程绑定;也可以不指定,不指定时,由系统自由分配CPU。

 1. 创建线程pthread_create

函数原型:

int  pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(start_rtn)(void), void *restrict arg);

返回值: 成功则返回0, 否则返回错误编号.

参数:

tidp: 指向新创建线程ID的变量, 作为函数的输出,此ID唯一指定线程,其他线程相关函数都需要此ID作为入参(线程同步函数不需要)。

attr: 用于定制各种不同的线程属性, NULL为默认属性.

start_rtn: 函数指针, 为线程开始执行的函数名.该函数可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由 pthread_join()获取

arg: 函数的唯一无类型(void)指针参数, 如要传多个参数, 可以用结构封装.

pthread_create 一般在main中调用,调用1次创建一个线程。多个线程做同一件事情,则第三个参数  函数指针可以相同;如果不同线程间做不同的事情,则函数指针不同,对应不同的task thread 函数。

线程创建之后,可以紧接着对各个线程指定CPU,也就是绑定;如果不绑定,则由系统自由分配。

2. 线程同步pthread_barrier_init/wait/destory

线程间的同步,需要用到以下三个函数,函数原型:

int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);
int pthread_barrier_wait(pthread_barrier_t *barrier);
int pthread_barrier_destroy(pthread_barrier_t *barrier);

其中,pthread_barrier_t是一个计数锁,对该锁的操作都包含在三个函数内部,我们不用关心也无法直接操作。只需要实例化一个对象丢给它就好。需要注意的是,参数是指针;一般直接实例化pthread_barrier_t  g_barrier,然后将 & g_barrier作为实参传递给函数。

pthread_barrierattr_t,锁的属性设置,设为NULL让函数使用默认属性即可。

count,你要指定的等待个数。比如需要4个线程间同步,则count设置为4。

pthread_barrier_init 和pthread_barrier_destroy,顾名思义,就是初始化和销毁,一般都在main中调用,且都只需要被调用一次。

pthread_barrier_wait是阻塞型等待,主要用于线程间同步。当代码执行到 pthread_barrier_wait的时候,先去check 是否所有的线程都在wait了,如果都在wait了,那么大家一起解锁。

pthread_barrier_wait则是在创建的线程函数(此处称之为 task thread函数)中被调用,在一个task thread函数中,可以多次调用pthread_barrier_wait,但需要保证在所有的task thread函数中调用相同次数。

3. 等待线程结束pthread_join

int pthread_join(pthread_t thread, void **retval);

描述 :pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

参数 :thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值,一般设为NULL。

返回值 : 0代表成功。 失败,返回的则是错误号

参考

4. 线程绑定CPU

参考

cpu亲和性(affinity)用于绑定到固定的CPU上。主要有以下函数。

int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);//进程绑定函数
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);//线程绑定函数
void CPU_ZERO (cpu_set_t *set)  /*这个宏对 CPU 集 set 进行初始化,将其设置为空集。*/
void CPU_SET (int cpu, cpu_set_t *set) /*这个宏将 指定的 cpu 加入 CPU 集 set 中*/
void CPU_CLR (int cpu, cpu_set_t *set) /*这个宏将 指定的 cpu 从 CPU 集 set 中删除。*/
int CPU_ISSET (int cpu, const cpu_set_t *set)
/*如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。*/
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,  cpu_set_t *cpuset);

线程绑定CPU的具体操作可以参考:

以下是一个参考网络,并自己修改过的例子

/******
编译时需要加pthread选项  g++ -g -Wall -pthread main.cpp -o sim

原实例在64bit机器上执行会报以下错误
   error: cast from ‘void*’ to ‘int’ loses precision [-fpermissive]
      int id = (int) param;
   因为在64bit机器上指针占8个字节,int占四个字节,从void* 到 int 的转换丢失精度
   改为int id = (long) param; 可编译通过
原实例 void* task(void* param) 没有返回值,可以添加return NULL;
如果想传递多个参数,可以用一个结构体进行封装
******/

#include <pthread.h>
#include <stdio.h>
#include <sys/sysinfo.h>
#include <unistd.h>
struct my_para {
    int id;
    long aux;
};
pthread_barrier_t b;
void* task(void* param) {
    // int id = (long) param;
    my_para * t_para = (my_para * ) param;
    int id = t_para->id;
    delete t_para;
    
    for (int i = 0 ; i < 2 ; ++i){
        pthread_barrier_wait(&b);
        printf("loop = %d , before the barrier %d\n", i, id);
        pthread_barrier_wait(&b);
        printf("loop = %d , after the barrier %d\n", i, id);
    }
    return NULL;
}
int main() {
    int nThread = sysconf(_SC_NPROCESSORS_ONLN);//return long, but dont care
    printf(" cpu num : %d \n", nThread);
    int i;
    pthread_t thread[nThread];
    cpu_set_t t_mask;
    pthread_barrier_init(&b, 0, nThread); //initial barrier
    for (i = 0; i < nThread; i++)
    {
        my_para * t_para  = new my_para;
        t_para->id  = i;
        t_para->aux = i  ;
        // thread[i] is returned by pthread_create 
        pthread_create(&thread[i], NULL , task, t_para);
        printf(" i = %d, thread pid = %d \n", i,  thread[i]);
        // pthread_create(&thread[i], 0, task, (void*)i);

        //bend thread function to CPU
        CPU_ZERO(&t_mask);
        CPU_SET(i,&t_mask);
        pthread_setaffinity_np(thread[i],sizeof(cpu_set_t),&t_mask);
    }
    for (i = 0; i < nThread; i++) {
        pthread_join(thread[i], NULL); //wait thread function finish
        printf(" thread finish \n" );
    }
    pthread_barrier_destroy(&b);  //destory barrier
    return 0;
}