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\n", 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\n", pthread_self());

    for (int i = 0; i < 5; i++) {
        printf("i = %d\n", 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\n", 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\n", 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\n", pthread_self());
    printf("child thread .....\n");
    int i;
    for (i = 0; i < 3; i++) 
        printf("child i = %d\n", 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\n", ret);
        printf("error information: %s\n", strerror(ret));
    }
    printf("parent pthread id: %lu\n", pthread_self());

    // 退出主线程, 子线程不受影响
    pthread_exit(NULL);

    printf("parent thread .....\n");
    for (i = 0; i < 3; i++) 
        printf("i = %d\n", 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\n", pthread_self());
    printf("child thread .....\n");
    number++;
    int i;
    for (i = 0; i < 3; i++) 
        printf("child i = %d\n", 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\n", ret);
        printf("error information: %s\n", strerror(ret));
    }
    printf("parent pthread id: %lu\n", pthread_self());

    int *ptr;
    // 阻塞等待子线程的结束, 并回收pch
    pthread_join(thid, (void**)&ptr);
    printf("++++++ number = %d\n", *ptr);

    printf("parent thread .....\n");
    for (i = 0; i < 3; i++) 
        printf("i = %d\n", 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\n", 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\n", 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\n", pthread_self());

        while (1) {
            printf("haha\n");
            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\n", ret);
        // 根据错误号打印错误信息
        printf("error information: %s\n", strerror(ret));
    }
    printf("parent pthread id: %lu\n", 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
*/
posted @ 2019-04-19 21:36  张飘扬  阅读(864)  评论(0编辑  收藏  举报