专栏首页程序员Linux下多线程的实现(基于pthread库)

Linux下多线程的实现(基于pthread库)

Linux内核在2.2版本中引入了类似线程的机制。Linux提供的vfork函数可以创建线程,此外Linux还提供了clone来创建一个线程,通过共享原来调用进程的地址空间,clone能像独立线程一样工作。Linux内核的独特,允许共享地址空间,clone创建的进程指向了父进程的数据结构,从而完成了父子进程共享内存和其他资源。clone的参数可以设置父子进程共享哪些资源,不共享哪些资源。实质上Linux内核并没有线程这个概念,或者说Linux不区分进程和线程。Linux喜欢称他们为任务。除了clone进程以外,Linux并不支持多线程,独立数据结构或内核子程序。但是POSIX标准提供了Pthread接口来实现用户级多线程编程。

关于线程更详细的介绍看这里:线程

POSIX下开发多线程主要依赖的就是Pthread。使用它需要包含头文件#include<pthread.h>。因为这个库在Pthread之中,在编译的时候需要加上参数:-lpthread.

线程的创建

pthread_create()函数用于创建一个线程。他的函数原型如下

extern int pthread_create (pthread_t *__restrict __newthread,
			   const pthread_attr_t *__restrict __attr,
			   void *(*__start_routine) (void *),
			   void *__restrict __arg);
  • __newthread是要创建线程的线程ID指针
  • __restrict __attr是创建线程时的属性
  • void *(*__start_routine) (void *)线程函数的入口地址
  • __arg是传递给线程函数的参数

函数返回值:调用成功后返回0,否则,创建线程失败。从第三个参数,也就是线程函数入口地址。从这儿可以知道线程函数的书写格式应该是具有void *类型的返回值,另外参数也是void *类型的。

线程创建以后以后的调度仍旧是不确定的。实际上,在Linux下线程ID是使用一个无符号长整型来表示的。

等待线程结束

pthread_join()函数用于等待线程结束,回收资源。类似于进程等待还是waitpid。

函数原型:int pthread_join(thread_t tid,void **status);

函数功能:tid是指定的要等待的线程ID,指定的线程必须位于当前进程之中,而且不能是分离线程。status指向线程退出状态的指针。

函数返回值:成功返回0,否则表示出现错误。

pthread_join只能适用于非分离的线程,因此如果没有必要等待线程终止,则应该将该线程分离。如果线程已经处于分离状态,那么调用失败。

线程终止

一个线程的终止有3种情况:

  • 线程调用了pthread_exit()函数退出
  • 线程被同一进程的其他线程取消
  • 线程从执行函数返回,返回值是线程退出码

有一个特殊情形是main所在的线程,我们称之为“初始线程”。从mian返回的时候,整个进程都被终止了,因此该进程所有的线程也被终止。还有就是在任意线程内调用exit函数会让该线程所在的进程整个退出。主动退出线程的时候一定要使用pthread_exit函数,而不是exit。pthread_exit在退出线程以后并不会释放资源,而是需要pthread_join函数来释放。当主线程调用这个pthread_exit函数仅仅只是终止主线程,其他线程仍将继续存在。

函数原型:void pthread_exit(void *retval)

参数retval可以通过pthread_join()来访问到这个指针。如果线程成功返回到启动它的线程,那么retval就会包含返回码,如果线程被取消,retval就会指向包含内容为PTHREAD_CANCELED的单元。如果对线程结束的返回值并不感兴趣,那么将retval设置为NULL即可。

一个测试程序如下:

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

void *fun(void *arg)        //线程函数
{
    printf("%s\n",(char *)arg);     //打印传递过来的参数
    printf("thread\n");
    pthread_exit(NULL);     //调用pthread_exit函数退出线程
    printf("test!\n");
}
int main()
{
    pthread_t id;
    int t = pthread_create(&id,NULL,fun,"argue");
    if(0 != t)
    {
        perror("pthread_create fail");
        exit(errno);
    }
    printf("main\n");
    pthread_join(id,NULL);      //等待线程执行返回

    return 0;
}

执行结果如下:

在编译源代码的时候需要链接pthread库,编译选项需要加上 -lpthread。

运行结果是正确的,主线程等待子线程结束,在子线程中调用了的pthread_exit函数结束了子线程,所以没有打印test!。

下面这个例子是对上面的这个例子一点小小的变化,可以通过pthread_join()来获取pthread_exit()的返回值。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

void *fun(void *arg)                //线程函数
{
    printf("%s\n",(char *)arg);     //打印传递过来的参数
    printf("thread\n");
    pthread_exit((void *)3);        //调用pthread_exit函数退出线程,并设置退出码

    printf("test!\n");              //不会执行到这里
}
int main()
{
    pthread_t id;
    void *status;

    int t = pthread_create(&id,NULL,fun,"arguement");
    if(0 != t)
    {
        perror("pthread_create fail");
        exit(errno);
    }

    printf("main\n");
    pthread_join(id,&status);      //等待线程执行返回
    printf("%ld\n",(long)status);   //打印线程退出码

    return 0;
}

线程取消

前面说过线程的终止包含3种情况,其中有一种就是线程被取消了。线程可以通过pthread_cancle来请求取消同一进程内的其他线程。

函数原型:int pthread_cancel(pthread_t thread);

该函数只是去请求取消,而不是命令取消。因此,默认情形下,他会使得线程取消。但是线程可以选择忽略或者控制如何取消。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 线程

    线程,有时候称为轻量级进程(lightweight process,LWP),是CPU使用的基本单元;它包含了线程ID,程序计数器,寄存器集合以及堆栈。它与属于...

    zy010101
  • POSIX读写锁

    续接上一篇“线程同步”:https://blog.csdn.net/zy010101/article/details/105967289

    zy010101
  • C++之const成员函数

    由于const函数的隐藏参数this指针变成了const 类名*this,所以const函数不能修改类的数据成员的值,但可以使用类的数据成员。

    zy010101
  • C# 温故而知新: 线程篇(三)下

    结果计时器会一直滚动,因为a对象被锁住,除非完成Thread.Sleep(3000000)后才能进入到a共享区 ? 由于以上的问题,微软还是建议我们使用一个私有...

    逸鹏
  • 给大伙来梳理下Java中的各种锁的分类

      大伙在面试的时候应该会经常碰到线程 并发方面的问题,而且也会问到你各种分布式锁的概念,本文就给大家整理了下各种锁的分类,希望对你有所帮助。

    用户4919348
  • Java多线程技术的9大知识点总结——精心整理

    1,进程和线程的概念。 |--进程:是程序的动态执行过程,它经历了从代码加载,执行,到执行完毕的一个完整过程。 |--线程:是实现并发机制的一个有效手段。 ? ...

    用户1289394
  • 多线程

    Thread 类位于 java.lang 中,表示进程中的执行线程。实现多线程有两种方式:

    Carlos Ouyang
  • 线程和进程基础——翻译文

    前言 所有的内容均来自:http://www.qnx.com/developers/docs/6.4.1/neutrino/getting_started/s...

    我没有三颗心脏
  • Java 多线程 从无到有

    个人总结:望对屏幕对面的您有所帮助 ? 一. 线程概述 进程: 有独立的内存控件和系统资源 应用程序的执行实例 启动当前电脑任务管理器:taskmgr 进程是...

    房上的猫
  • 线程的生命周期

    线程的六种状态: NEW、RUNNABLE、BIOCKED、WAITING、TIME_WAITING、TERMINATED。

    用户7386338

扫码关注云+社区

领取腾讯云代金券