zoukankan      html  css  js  c++  java
  • C/C++ Pthread线程

    线程按照其调度者可以分为用户级线程和核心级线程两种 
    用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持; 
    我们常用基本就是用户级线程,所以就只总结一下POSIX提供的用户级线程接口; 
    基本线程操作相关的函数: 
    1线程的建立结束 
    2线程的互斥和同步 
    3使用信号量控制线程 
    4线程的基本属性配置 

    基本线程操作:

    函数说明
    pthread_create() 创建线程开始运行相关线程函数,运行结束则线程退出
    pthread_eixt() 因为exit()是用来结束进程的,所以则需要使用特定结束线程的函数
    pthread_join() 挂起当前线程,用于阻塞式地等待线程结束,如果线程已结束则立即返回,0=成功
    pthread_cancel() 发送终止信号给thread线程,成功返回0,但是成功并不意味着thread会终止
    pthread_testcancel() 在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
    pthread_setcancelstate() 设置本线程对Cancel信号的反应
    pthread_setcanceltype() 设置取消状态 继续运行至下一个取消点再退出或者是立即执行取消动作
    pthread_setcancel() 设置取消状态


    互斥与同步机制基本函数

    函数说明
    pthread_mutex_init() 互斥锁的初始化
    pthread_mutex_lock() 锁定互斥锁,如果尝试锁定已经被上锁的互斥锁则阻塞至可用为止
    pthread_mutex_trylock() 非阻塞的锁定互斥锁
    pthread_mutex_unlock() 释放互斥锁
    pthread_mutex_destory() 互斥锁销毁函数


    信号量线程控制(默认无名信号量)

    函数说明
    sem_init(sem) 初始化一个定位在sem的匿名信号量
    sem_wait() 把信号量减1操作,如果信号量的当前值为0则进入阻塞,为原子操作
    sem_trywait() 如果信号量的当前值为0则返回错误而不是阻塞调用(errno=EAGAIN),其实是sem_wait()的非阻塞版本
    sem_post() 给信号量的值加1,它是一个“原子操作”,即同时对同一个信号量做加1,操作的两个线程是不会冲突的
    sem_getvalue(sval) 把sem指向的信号量当前值放置在sval指向的整数上
    sem_destory(sem) 销毁由sem指向的匿名信号量


    线程属性配置相关函数

    函数说明
    pthread_attr_init() 初始化配置一个线程对象的属性,需要用pthread_attr_destroy函数去除已有属性
    pthread_attr_setscope() 设置线程属性
    pthread_attr_setschedparam() 设置线程优先级
    pthread_attr_getschedparam() 获取线程优先级

    基本的线程建立运行pthread_create

    /* thread.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    #define THREAD_NUMBER       3                 /*线程数*/
    #define REPEAT_NUMBER       5                 /*每个线程中的小任务数*/
    #define DELAY_TIME_LEVELS  10.0             /*小任务之间的最大时间间隔*/
    //
    void *thrd_func(void *arg) { 
        /* 线程函数例程 */
        int thrd_num = (int)arg;
        int delay_time = 0;
        int count = 0;
        printf("Thread %d is starting
    ", thrd_num);
        for (count = 0; count < REPEAT_NUMBER; count++) {
            delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
            sleep(delay_time);
            printf("	Thread %d: job %d delay = %d
    ", 
                          thrd_num, count, delay_time);
        }
    
        printf("Thread %d finished
    ", thrd_num);
        pthread_exit(NULL);
    }
    
    int main(void) {
         pthread_t thread[THREAD_NUMBER];
         int no = 0, res;
         void * thrd_ret;
         srand(time(NULL));    
         for (no = 0; no < THREAD_NUMBER; no++) {
              /* 创建多线程 */
              res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
              if (res != 0) {
                   printf("Create thread %d failed
    ", no);
                   exit(res);
              }
         }
    
         printf("Create treads success
     Waiting for threads to finish...
    ");
         for (no = 0; no < THREAD_NUMBER; no++) {
              /* 等待线程结束 */
              res = pthread_join(thread[no], &thrd_ret);
              if (!res) {
                printf("Thread %d joined
    ", no);
              } else {
                printf("Thread %d join failed
    ", no);
              }
         }
         return 0;        
    }

    例程中循环3次建立3条线程,并且使用pthread_join函数依次等待线程结束; 
    线程中使用rand()获取随机值随机休眠5次,随意会出现后执行的线程先执行完成; 
    运行结果:

    $ gcc thread.c -lpthread
    $ ./a.out 
    Create treads success
     Waiting for threads to finish...
    Thread 0 is starting
    Thread 1 is starting
    Thread 2 is starting
    Thread 1: job 0 delay = 2
    Thread 1: job 1 delay = 2
    Thread 0: job 0 delay = 8
    Thread 2: job 0 delay = 10
    Thread 2: job 1 delay = 3
    Thread 1: job 2 delay = 10
    Thread 0: job 1 delay = 8
    Thread 0: job 2 delay = 3
    Thread 0: job 3 delay = 1
    Thread 2: job 2 delay = 8
    Thread 1: job 3 delay = 8
    Thread 1: job 4 delay = 1
    Thread 1 finished
            Thread 2: job 3 delay = 6
            Thread 0: job 4 delay = 7
    Thread 0 finished
    Thread 0 joined
    Thread 1 joined
            Thread 2: job 4 delay = 10
    Thread 2 finished
    Thread 2 joined

    可以看到,线程1先于线程0执行,但是pthread_join的调用时间顺序,先等待线程0执行; 
    由于线程1已经早结束,所以线程0被pthread_join等到的时候,线程1已结束,就在等待到线程1时,直接返回; 

    线程执行的互斥和同步pthread_mutex_lock

    在上面的程序中增加互斥锁

    /*thread_mutex.c*/
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    #define THREAD_NUMBER        3            /* 线程数 */
    #define REPEAT_NUMBER        3            /* 每个线程的小任务数 */
    #define DELAY_TIME_LEVELS 10.0         /*小任务之间的最大时间间隔*/
    pthread_mutex_t mutex;
    
    void *thrd_func(void *arg) {
         int thrd_num = (int)arg;
         int delay_time = 0, count = 0;
         int res;
         /* 互斥锁上锁 */
         res = pthread_mutex_lock(&mutex);
         if (res) {
              printf("Thread %d lock failed
    ", thrd_num);
              pthread_exit(NULL);
         }
         printf("Thread %d is starting
    ", thrd_num);
         for (count = 0; count < REPEAT_NUMBER; count++) {          
             delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
             sleep(delay_time);
             printf("	Thread %d: job %d delay = %d
    ", 
                                          thrd_num, count, delay_time);
         }
         printf("Thread %d finished
    ", thrd_num);
         pthread_exit(NULL);
    }
    
    int main(void) {
         pthread_t thread[THREAD_NUMBER];
         int no = 0, res;
         void * thrd_ret;
    
         srand(time(NULL));
         /* 互斥锁初始化 */
         pthread_mutex_init(&mutex, NULL);
         for (no = 0; no < THREAD_NUMBER; no++) {
              res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
              if (res != 0) {
                  printf("Create thread %d failed
    ", no);
                  exit(res);
              }
         }     
         printf("Create treads success
     Waiting for threads to finish...
    ");
         for (no = 0; no < THREAD_NUMBER; no++) {
              res = pthread_join(thread[no], &thrd_ret);
              if (!res) {
                    printf("Thread %d joined
    ", no);
              } else  {
                  printf("Thread %d join failed
    ", no);
              }
         }   
         /****互斥锁解锁***/
         pthread_mutex_unlock(&mutex);
         pthread_mutex_destroy(&mutex);          
         return 0;        
    }

    在上面的例程中直接添加同步锁pthread_mutex_t; 
    在线程中加入,于是程序在执行线程程序时; 
    调用pthread_mutex_lock上锁,发现上锁时候后进入等待,等待锁再次释放后重新上锁; 
    所以线程程序加载到队列中等待,等待成功上锁后继续执行程序代码; 
    运行结果

    $gcc thread_mutex.c -lpthread
    $ ./a.out 
    Create treads success
     Waiting for threads to finish...
    Thread 0 is starting
            Thread 0: job 0 delay = 9
            Thread 0: job 1 delay = 4
            Thread 0: job 2 delay = 7
    Thread 0 finished
    Thread 0 joined
    Thread 1 is starting
            Thread 1: job 0 delay = 6
            Thread 1: job 1 delay = 4
            Thread 1: job 2 delay = 7
    Thread 1 finished
    Thread 1 joined
    Thread 2 is starting
            Thread 2: job 0 delay = 3
            Thread 2: job 1 delay = 1
            Thread 2: job 2 delay = 6
    Thread 2 finished
    Thread 2 joined

    跟例程1中执行结果不同,线程程序被加载到队列中而不能马上执行,需要等到能够成功上锁; 
    上锁后,继续执行线程程序,上锁执行; 
    这样线程被依次执行的情况在实际使用场景中经常出现; 
    使用场景: 
    当用户登录后获取秘钥才能继续获取该用户的基本信息时;需要等待登录线程结束后才能继续执行获取用户信息的线程时, 
    需要调用两条线程,假如是:threadLogin(),threadGetInfo(); 则可以有2种方法实现: 
    1 此时可以使用互斥锁同时一次性调用完threadLogin()和threadGetInfo(); 
    2 当然也可以不使用互斥锁直接在threadLogin()中登录验证成功后调用threadGetInfo(); 
    相比之下,方式1更加清晰的显示逻辑关系,增加代码可读性可扩展性。

    使用信号量控制线程的执行顺序sem_post

    修改上面例程,上面的是使用pthread_mutex_lock互斥锁控制线程执行顺序, 
    使用另外一种线程执行顺序的控制;

    /* thread_sem.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <semaphore.h>
    
    #define THREAD_NUMBER       3
    #define REPEAT_NUMBER       3
    #define DELAY_TIME_LEVELS   10.0
    
    sem_t sem[THREAD_NUMBER];
    
    void * thrd_func(void *arg) {
        int thrd_num = (int)arg;
        int delay_time = 0;
        int count = 0;
        sem_wait(&sem[thrd_num]);
        printf("Thread %d is starting
    ", thrd_num);
        for (count = 0; count < REPEAT_NUMBER; count++) {
            delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
            sleep(delay_time);
            printf("	Thread %d: job %d delay = %d
    ", thrd_num, count, delay_time);
        }
        printf("Thread %d finished
    ", thrd_num);
        pthread_exit(NULL);
    }
    
    int main(void) {
        pthread_t thread[THREAD_NUMBER];
        int no = 0, res;
        void * thrd_ret;
        srand(time(NULL));
        for (no = 0; no < THREAD_NUMBER; no++) {
            sem_init(&sem[no], 0, 0);
            res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);
            if (res != 0) {
                printf("Create thread %d failed
    ", no);
                exit(res);
            }
        }
    
        printf("Create treads success
     Waiting for threads to finish...
    ");
        sem_post(&sem[THREAD_NUMBER - 1]);
        for (no = THREAD_NUMBER - 1; no >= 0; no--) {
            res = pthread_join(thread[no], &thrd_ret);
            if (!res) {
                printf("Thread %d joined
    ", no);
            } else {
                printf("Thread %d join failed
    ", no);
            }
            sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);           
        }
    
        for (no = 0; no < THREAD_NUMBER; no++) {
            sem_destroy(&sem[no]);      
        }
        return 0;        
    }

    执行结果,仍然是建立3条线程,每条线程执行时休眠随机时长:

    $ gcc thread_sem.c -lpthread 
    $ ./a.out 
    Create treads success
     Waiting for threads to finish...
    Thread 2 is starting
            Thread 2: job 0 delay = 9
            Thread 2: job 1 delay = 9
            Thread 2: job 2 delay = 5
    Thread 2 finished
    Thread 2 joined
    Thread 1 is starting
            Thread 1: job 0 delay = 5
            Thread 1: job 1 delay = 7
            Thread 1: job 2 delay = 4
    Thread 1 finished
    Thread 1 joined
    Thread 0 is starting
            Thread 0: job 0 delay = 3
            Thread 0: job 1 delay = 9
            Thread 0: job 2 delay = 8
    Thread 0 finished
    Thread 0 joined

    执行结果与第2个例程非常相似,只不过教材中进行倒序执行而已; 
    那么这种方式其实与使用互斥锁相比,代码量可读性基本持平不相上下;

    线程的基本属性pthread_attr_setscope

    设置属性一般有: 
    1 绑定属性 
    2 分离属性 
    3 堆栈地址 
    4 堆栈大小 
    5 优先级

    关于绑定属性就是绑定于内核线程; 
    分离属性主要是讲线程结束后是否马上释放相应的内存;

    /* thread_attr.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    #define THREAD_NUMBER       1
    #define REPEAT_NUMBER       3
    #define DELAY_TIME_LEVELS   10.0
    int finish_flag = 0;
    
    void * thrd_func(void * arg){
        int delay_time = 0;
        int count = 0;
        printf("Thread is starting
    ");
        for (count = 0; count < REPEAT_NUMBER; count++) {
            delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
            sleep(delay_time);
            printf("	Thread : job %d delay = %d
    ", count, delay_time);
        }
        printf("Thread finished
    ");
        finish_flag = 1;
        pthread_exit(NULL);
    }
    
    int main(void) {
        pthread_t thread;
        pthread_attr_t attr;
        int res = 0;
        srand(time(NULL));
        res = pthread_attr_init(&attr);
        if (res != 0) {
            printf("Create attribute failed
    ");
            exit(res);
        }
        res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
        res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        if (res != 0) {
            printf("Setting attribute failed
    ");
            exit(res);
        }
        res = pthread_create(&thread, &attr, thrd_func, NULL);
        if (res != 0) {
            printf("Create thread failed
    ");
            exit(res);
        }
        pthread_attr_destroy(&attr);
        printf("Create tread success
    ");
    
        while(!finish_flag){
            printf("Waiting for thread to finish...
    ");
            sleep(2);
        }
        return 0;        
    }

    在运行前后使用 $ free 命令查看内存前后的使用情况发现: 
    在线程结束后内存马上被释放;
    其实,一般线程的属性直接使用系统默认属性即可; 
    关于线程的使用,大约就是这样。

  • 相关阅读:
    多态实现--虚函数与纯虚函数
    CentOS6编译安装gcc高版本
    Linux多进程CS服务器简单测试
    Linux单用户CS模型TCP通讯完全注释手册
    进程线程及其状态
    Java学习-字符串、字符、ASCII、数字的互相转换
    Java学习-素数
    Java学习-日期
    Java学习-多态
    Java学习-练习
  • 原文地址:https://www.cnblogs.com/zhangjiansheng/p/9332799.html
Copyright © 2011-2022 走看看