zoukankan      html  css  js  c++  java
  • pthread

    基础API

    头文件: #include <pthread.h>

    失败返回错误号, 不能用perror打印错误信息, 使用strerror(ret)

    pthread_self

    pthread_t pthread_self(void);
    返回线程ID, 在linux下是无符号整数(%lu), 在其他系统中可能是结构体实现
    线程ID是内部标识, 两个进程间, 线程ID可以相同

    pthread_create

    int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    创建线程: pthread_create, 函数调用成功返回0,
    参数:
      thread: 传出参数, 保存系统分配的ID
      attr: 线程属性, 通常传NULL. 默认父子进程不分离, 主线程要手动释放子进程pcb, 即使用pthread_join
      stat_routine: 子线程的处理函数
      arg: 回调函数的参数

    pthread_exit

    void pthread_exit(void *retval);
    单个线程退出, 主线程执行pthread_exit不影响子线程的执行
    主线程执行exit或return, 则子线程都会退出
    子线程执行exit, 所有线程(包括主线程)都会推出
    子线程执行return和pthread_exit都是退出当前线程

    参数:
      retval: 必须指向全局或堆的地址空间, 不能是一个栈地址

    pthread_join

    int pthread_join(pthread_t thread, void **retval);
    阻塞等待线程退出, 获取线程退出状态
    参数:
      thread: 要回收的子线程的线程id, 不是指针
      reval: 读取线程退出的时候携带的状态信息, 指向的内存和pthread_exit或return参数指向的内存一样; 如果线程使用pthread_cancel异常终止, reval所指向的单元里存放常数PTHREAD_CANCELED

    pthread_detach

    int pthread_detach(pthread_t thread); 线程分离
    调用该函数之后不需要pthread_join, 子线程会自动回收自己的pcb, 一般在创建线程时设置分离属性

    pthread_cancel

    int pthread_cancel(pthread_t thread); 杀死(取消)线程, 可杀死分离的线程
    使用注意事项: 在要杀死的子线程对应的处理的函数的内部, 必须做过一次系统调用(使用类似printf的函数), 即不是实时的杀死线程; 可以使用pthread_testcancel()来添加取消点

    pthread_equal

    int pthread_equal(pthread_t t1, pthread_t t2);
    比较两个线程ID是否相等(预留函数)

    线程属性的设置

    通过属性设置线程的分离
    线程属性类型: pthread_attr attr;
    线程属性操作函数
      int pthread_attr_init(pthread_attr_t *attr);
    对线程属性变量的初始化
      int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 设置线程分离属性
        attr: 线程属性
        detachstate: PTHREAD_CREATE_DETACHED(分离), PTHREAD_CREATE_DETACHED(非分离)
      int pthread_attr_destroy(pthread_attr_t *attr); 释放线程资源

    使用注意事项

    1. 主线程退出其他线程不退出, 主线程应调用pthread_exit
    2. 避免僵尸线程: pthread_join, pthread_detach, pthread_create指定分离属性
    3. malloc和mmap申请的内存可以被其他线程释放
    4. 应避免在多线程模型中调用fork, 除非马上exec, 子进程中只有调用fork的线程存在, 其他线程在子进程中均pthread_exit-->不太懂
    5. 信号的复杂语义很难和多线程共存, 应避免在多线程引入信号机制

    实例程序

    pthread_create使用

    输出顺序不确定

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    void *myfunc(void * arg) {
        // 打印子线程ID
        printf("child thread id: %lud
    ", pthread_self());
        return NULL;
    }
    
    int main(int argc, const char * argv[]) {
        // 创建一个子线程
        // 线程ID对应的变量
        pthread_t pthid;
        pthread_create(&pthid, NULL, myfunc, NULL);
        printf("parent thread id: %lud
    ", pthread_self());
    
        for (int i = 0; i < 5; i++) {
            printf("i = %d
    ", i);
        }
    
        sleep(2);	
    
        return 0;
    }
    

    传值与传地址

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    void *myfunc(void *arg) {
        //int num = *(int*)arg;
        int num = (int)arg;
        
        // 打印子线程ID
        printf("%dth child thread id: %lu
    ", num, pthread_self());
    }
    
    int main(void) {
        pthread_t pthid[5];
        int i;
    
        for (i = 0; i < 5; i++) {
            //pthread_create(&pthid[i], NULL, myfunc, (void*)&i);
            // 由于int存储空间和指针的存储空间相同所以可以这么传 
            pthread_create(&pthid[i], NULL, myfunc, (void*)i);  
        }
        printf("parent thread id: %lu", pthread_self());
    
        for (i = 0; i < 5; i++)         // 子线程不会执行接下来的for循环
            printf("i = %d
    ", i);
    
        sleep(2);
    
        return 0;
    }
    

    主线程先退出

    主线程先退出, 子线程正常执行

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    void *myfunc(void *arg) {
        printf("child pthread id: %lu
    ", pthread_self());
        printf("child thread .....
    ");
        int i;
        for (i = 0; i < 3; i++) 
            printf("child i = %d
    ", i);
        
        //return NULL;
    }
    
    int main(void) {
        int i;
        pthread_t thid;
        int ret = pthread_create(&thid, NULL, myfunc, NULL);
    
        if (ret != 0) {
            printf("error number: %d
    ", ret);
            printf("error information: %s
    ", strerror(ret));
        }
        printf("parent pthread id: %lu
    ", pthread_self());
    
        // 退出主线程, 子线程不受影响
        pthread_exit(NULL);
    
        printf("parent thread .....
    ");
        for (i = 0; i < 3; i++) 
            printf("i = %d
    ", i);
    
        return 0;
    }
    
    /*
    parent pthread id: 139948367210304
    child pthread id: 139948358948608
    child thread .....
    child i = 0
    child i = 1
    child i = 2
    */
    

    join与pthread_exit

    没有return返回和return NULL结果一样都是core dumped
    使用return &numberpthread_exit(&number)时, 主线程的pthread_join(thid, (void**)&ptr);都可以收到number的值

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    int number = 100;
    
    void *myfunc(void *arg) {
        printf("child pthread id: %lu
    ", pthread_self());
        printf("child thread .....
    ");
        number++;
        int i;
        for (i = 0; i < 3; i++) 
            printf("child i = %d
    ", i);
    
    	// return NULL;		// Segmentation fault (core dumped)
        //return &number;
        pthread_exit(&number);
    }
    
    int main(void) {
        int i;
        pthread_t thid;
        int ret = pthread_create(&thid, NULL, myfunc, NULL);
    
        if (ret != 0) {
            printf("error number: %d
    ", ret);
            printf("error information: %s
    ", strerror(ret));
        }
        printf("parent pthread id: %lu
    ", pthread_self());
    
        int *ptr;
        // 阻塞等待子线程的结束, 并回收pch
        pthread_join(thid, (void**)&ptr);
        printf("++++++ number = %d
    ", *ptr);
    
        printf("parent thread .....
    ");
        for (i = 0; i < 3; i++) 
            printf("i = %d
    ", i);
    
        return 0;
    }
    
    /*
    parent pthread id: 139819023099712
    child pthread id: 139819014838016
    child thread .....
    child i = 0
    child i = 1
    child i = 2
    ++++++ number = 101
    parent thread .....
    i = 0
    i = 1
    i = 2
    
    */
    

    两个线程数

    当线程A数的数字还没有写入内存中时, 就已经失去cpu
    当线程B数的数字同步到内存中时, 会导致共享的数据混乱

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    #define MAX 1000
    // 全局变量
     int number;
    
     // 线程处理函数
    void* funcA_num(void* arg)
    {
        for(int i=0; i<MAX; ++i)
        {
            int cur = number;
            cur++;
            number = cur;
            printf("Thread A, id = %lu, number = %d
    ", pthread_self(), number);
            usleep(10);
        }
    
        return NULL;
    }
    
    void* funcB_num(void* arg)
    {
        for(int i=0; i<MAX; ++i)
        {
            int cur = number;
            cur++;
            number = cur;
            printf("Thread B, id = %lu, number = %d
    ", pthread_self(), number);
            usleep(10);
        }
    
        return NULL;
    }
    
    int main(int argc, const char* argv[])
    {
            pthread_t p1, p2;
    
            // 创建两个子线程
            pthread_create(&p1, NULL, funcA_num, NULL);
            pthread_create(&p2, NULL, funcB_num, NULL);
    
            // 阻塞,资源回收
            pthread_join(p1, NULL);
            pthread_join(p2, NULL);
    
            return 0;
    }
    /*
    number可能数的数字不足
    */
    

    使用pthread_cancel杀死分离的线程

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <pthread.h>
    
    void* myfunc(void* arg)
    {
            printf("child pthread id: %lu
    ", pthread_self());
    
            while (1) {
                printf("haha
    ");
                sleep(2);
            }
    
            return NULL;
                
    }
    
    int main(int argc, const char* argv[])
    {
        pthread_t thid;
        // 初始化线程属性
        pthread_attr_t attr;
        pthread_attr_init(&attr);
    
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        // 返回错误号
        int ret = pthread_create(&thid, &attr, myfunc, NULL);
        if(ret != 0)
        {
            printf("error number: %d
    ", ret);
            // 根据错误号打印错误信息
            printf("error information: %s
    ", strerror(ret));
        }
        printf("parent pthread id: %lu
    ", pthread_self());
    
        pthread_cancel(thid);
        sleep(1);
    
        pthread_attr_destroy(&attr);
        pthread_exit(NULL);
        //return 0;
    }
    /*
    parent pthread id: 140625550829376
    child pthread id: 140625542567680
    haha
    */
    
  • 相关阅读:
    快速开发框架:进销存业务注意事项
    延时执行函数:前浪死在沙滩上
    新增筛选方案
    进销存数据库设计:采购订单
    SasS 设计原则十二因素
    四种线程池的解析
    高并发下的流量控制
    Mybatis 缓存机制
    谈谈JVM垃圾回收
    如何使错误日志更加方便地排查问题
  • 原文地址:https://www.cnblogs.com/hesper/p/10738977.html
Copyright © 2011-2022 走看看