LINUX编程-多线程

1. 线程的概念

1.1 什么是线程

轻量级进程,本质仍然是进程,都是通过clone函数实现的。
进程:独立的地址空间,拥有PCB,最小的资源分配单元
线程:没有独立的地址空间,拥有PCB,最小的执行单元

1.2 线程共享资源

  • 文件描述符表
  • 每种信号的处理方式
  • 当前工作目录
  • 用户ID和组ID
  • 内存地址空间

1.3 线程非共享资源

  • 线程id
  • 处理现场和栈指针
  • 独立的栈空间
  • error变量
  • 信号屏蔽字
  • 调度优先级

1.4 线程优缺点

优点:提高并发性、占用资源小、通信方便
缺点:库函数不稳定、编写和调试困难、对信号支持不好

2. 线程的操作函数

2.1 pthread_create函数

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);

功能:创建线程
函数参数:

  • pthread_t *thread 线程ID,传出参数
  • const pthread_attr_t *attr 线程的属性信息,一般用不到的
  • void *(*start_routine) (void *) 线程函数,是一个void *func(void *)形式的函数指针
  • void *arg 函数执行的参数
  • 成功返回0,失败返回errno

注:编译需要添加pthread库

2.2 phtread_self函数

#include <pthread.h>
pthread_t pthread_self(void);

功能:返回调用函数的ID,这个值与调用pthread_create返回的pthread_t *thread一样。

2.3 pthread_exit函数

 #include <pthread.h>
 void pthread_exit(void *retval);

功能:终止调用的进程函数
函数参数:

  • void *retval:指向的数据将作为线程退出时的返回值,主线程中的 pthread_join() 函数都可以接收到线程的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。

pthread_exit和return的区别:

  1. pthread_exit() 可以自动调用线程清理程序(本质是一个由 pthread_cleanup_push() 指定的自定义函数),return 则不具备这个能力。
  2. 在主线程中, return 语句不仅会终止主线程执行,还会终止其它子线程执行。pthread_exit() 函数只会终止当前线程,不会影响进程中其它线程的执行。

2.4 pthread_join函数

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

功能:获取某个线程执行结束时返回的数据,pthread_join会一直阻塞调用它的线程,接收到目标线程的返回值,阻塞状态才会解除。
参数:

  • thread代表创建的时候传出的第一个参数
  • retval代表传出线程退出的信息

注:一个线程执行结束的返回值只能由一个 pthread_join() 函数获取,当有多个线程调用 pthread_join() 函数获取同一个线程的执行结果时,哪个线程最先执行 pthread_join() 函数,执行结果就由那个线程获得,其它线程的 pthread_join() 函数都将执行失败。

创建线程demo

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thr(void *args)
{
    printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
    sleep(5);
    return (void*)0;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, thr, nullptr);
    printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
    
    int ret = 0;
    if ((ret = pthread_join(tid, nullptr)) > 0) { // 阻塞等待线程回收
        printf("join err : %d, %s\n", ret, strerror(ret));
    }
    
    printf("I will exit.\n");
    pthread_exit(nullptr);
    return 0;
}

线程退出注意事项:

  • 可以在线程中使用pthread_exit
  • 可以在线程中使用return(主控线程return代表退出进程)
  • exit代表退出整个进程
  • pthread_exit或者return返回的指针所指的内存单元必须是全局的或者是在堆空间分配的,不能是在栈空间分配的。
  • 主线程调用pthread_exit会阻塞等待其余线程都结束之后再退出。

2.5 pthread_detach函数

#include <pthread.h>
int pthread_detach(pthread_t thread);

功能:线程分离,线程主动与主线程断开关系,线程结束之后,其退出状态不由其他线程获取,而是自己直接自动释放,网络、多线程服务常用。pthread有两种状态joinable状态和unjoinable状态,一个线程默认的状态是joinable。如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当调用了pthread_join之后这些资源才会被释放。若是
unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。如果线程状态为 joinable,需要在之后适时调用pthread_join。
参数:
pthread_t thread:
注:不能对一个已经分离的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,一个线程调用了pthread_detach就不能再调用pthread_join。

线程分离函数demo:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *args)
{
    printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
    sleep(5);
    return (void*)0;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, thr, nullptr);
    printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
    
    pthread_detach(tid); // 线程分离
    sleep(5);
    
    int ret = 0;
    if ((ret = pthread_join(tid, nullptr)) > 0) {
        printf("join err : %d, %s\n", ret, strerror(ret));
    }

    printf("I will exit.\n");
    pthread_exit(nullptr);
    return 0;
}

pthread_join函数阻塞等待回收失败

Im main, pid = 525576, tid = 3930953536.
Im a thread, pid = 525576, tid = 3922568960.
join err : 22, Invalid argument
I will exit.

2.6 pthread_cancel函数

#include <pthread.h>
int pthread_cancel(pthread_t thread);

功能:仅仅是向目标线程发送 Cancel 信号,至于目标线程是否处理该信号以及何时结束执行,由目标线程决定。对于接收 Cancel 信号后结束执行的目标线程,等同于该线程自己执行pthread_exit(PTHREAD_CANCELED)。也就是说,当一个线程被强制终止执行时,它会返回 PTHREAD_CANCELED 。因此,当对一个cancel的进程进行pthrean_join回收时,得到的返回值为-1
参数:

  • pthread_t thread:用于指定发送 Cancel 信号的目标线程。

杀死线程函数demo:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *args)
{
    while(1) {
        printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
        sleep(1);
    }
    return (void*)0;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, thr, nullptr);
    printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
    sleep(5);
    pthread_cancel(tid);

    void *ret;
    ret = pthread_join(tid, &ret);
    printf("thread exit with : %d\n", (int)ret);
    return 0;
}

2.7 进程属性控制函数

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr); // 初始化线程属性
int pthread_attr_destroy(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);

功能: 设置属性分离状态
参数:

  • attr init 初始化的属性
  • detachstate
    PTHREAD_CREATE_DETACHED 线程分离
    PTHREAD_CREATE_JOINABLE 允许回收

设置线程属性demo

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *arg)
{
    printf("I am a thread.\n");
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_attr_t attr;
    pthread_attr_init(&attr); // 初始化属性
    pthread_attr_getdetachstate(&arr, PTHREAD_CREATE_DETACHED); //设置属性分离

    pthread_t tid;
    pthread_create(&tid, &attr, thr, NULL);
    int ret = 0;
    if ((ret = pthread_join(tid, NULL)) > 0) {
        printf("join err: %d, %s\n", ret, strerror(ret));
    }

    pthread_attr_destroy(&attr);
}

其他:

  • alias echomake = ‘cat ~/bin/makefile.template >> makefile’
  • set -o vi 设置shell里vi的快捷键,设置后vi的快捷键可以在shell中使用
  • 查看线程库版本:getconf GNU_LIBPTHREAD_VERSION
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值