进程是计算机分配资源的基本单位,线程是cpu调度的基本单位

线程基本概念:

LWP:light weight process 轻量级的进程。创建线程的底层函数和进程一样,都是clone,因此线程的本质仍是进程(在linux环境下)

与进程相比,线程有独立的TCB结构体(类似于进程的PCB),但没有独立的地址空间(共享),类似于合租与独居。

查看线程号(LWP,不是TID)可以用下述命令:

ps -Lf xxx(PID)

线程创建相关函数

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
//=================================属性设置按需要采用,若普通情况可以不用设置===============//
pthread_attr_t attr;                                // 定义线程的属性变量
int pthread_attr_init(pthread_attr_t *attr);        // 初始化属性变量
int pthread_attr_setXXX(pthread_attr_t *attr, ...); // 设置属性变量的值
//此处仅列举常用的//
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // 设置/获得线程的分离属性
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); // 设置/获得是否继承创建者的调度策略
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); // 设置/获得调度策略
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); // 设置/获得线程的静态优先级
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
//=================================上述属性设置按需要采用,若普通情况可以不用设置===============//

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
// 创建线程,此处void *(*start_routine)(void *)函数指针指向的函数,void *arg作为其参数,并且需要采用值传递,如果数据类型不同需要强转
int pthread_detach(pthread_t thread); // 如果没有设置线程的分离属性,可以用此函数强制设为分离属性,防止线程结束后变为僵尸进程,无法释放资源,而且不能再用 pthread_join等待回收该进程的资源

pthread_t pthread_self(void);         // 创建成功后,可以在线程中使用,获取当前线程的tid
void pthread_exit(void *retval);      // 退出线程,并得到返回值retval
int pthread_cancel(pthread_t thread); // 给指定线程发送一个取消的请求
int pthread_setcancelstate(int state, int *oldstate);
// 设置线程的取消状态:PTHREAD_CANCEL_ENABLE 可取消; PTHREAD_CANCEL_DISABLE 不可取消
int pthread_setcanceltype(int type, int *oldtype);
// 设置线程的取消类型:PTHREAD_CANCEL_DEFERRED 延时响应; PTHREAD_CANCEL_ASYNCHRONOUS 立即响应。

int pthread_join(pthread_t thread, void **retval); // 接合指定已结束或者待结束的线程,并得到返回状态值;如果指定的线程还在运行,将会阻塞等待。

应用实例

char *retval = "byebye!\n"; // 假设为共享资源
pthread_mutex_t m;          // 定义互斥锁

void handler(void *arg)
{
    pthread_mutex_unlock(&m);
}

void *child_thread(void *arg)
{

    while (1)
    {
        pthread_cleanup_push(handler, NULL); // 上锁前,需要将handler函数压入线程取消处理的栈中,以防止该子进程在运行的中途被取消,造成死锁
        pthread_mutex_lock(&m);
        printf("Child thread obtain the mutex:%s\n", retval);
        pthread_mutex_unlock(&m);
        pthread_cleanup_pop(0); // 解锁后,将handler从栈中弹出,不执行
        sleep(2);
    }
}

int main()
{
    pthread_mutex_init(&m, NULL);

    pthread_t tidofparent = pthread_self(); // 接收主线程的tid值。
    printf("parent's tid:%ld\n", tidofparent);
    printf("parent's tid:%ld\n", sizeof(int));

    pthread_t tid;
    pthread_create(&tid, NULL, child_thread, NULL); // 新建子线程
    // pthread_detach(tid); 让线程“自立门户”,结束后资源自动回收,此处与pthread_cancel和pthread_join冲突,
    sleep(5);
    pthread_cancel(tid); // 5秒后,向子线程发送取消的请求

    // 待子线程被取消后,锁被handler自动释放,可以继续加互斥锁,对公共资源进行操作
    pthread_mutex_lock(&m);
    retval = "I'm main pthread!\n";
    printf("Now I botain the mutex:%s\n", retval);
    pthread_mutex_unlock(&m);
    /*
    void *p;
    pthread_join(tid, &p); // 如果没有采用pthread_cancel(tid)取消子进程,这部分将会阻塞等待子线程结束,接受子线程的返回值并打印
    printf("子线程的返退出回值:%s\n", (char *)p);
*/
    return 0;
}

注意事项

1.主线程退出也可以用pthread_exit(),其他子线程照样运行不受影响。但如果主线程运行了return/exit等语句,或者子线程用exit,会导致整个进程退出。

2.避免线程编程僵尸进程的三种方法:

  • int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) 提前设置分离属性
  • int pthread_detach(pthread_t thread);子线程创建后强制设置分离属性
  • 在线程结束后利用pthread_join()进行接合。

3.malloc 和mmap 申请的内存可以被其他子线程释放,因为申请出来的都是共享堆地址。

4.信号语义复杂,尽量避免和线程机制混用。