linux下如何随时创建新线程,linux 线程创建

一、线程的概念

进程在各个独立的地址空间中运行,进程之间共享数据需要mmap或者进程间通信机制,本节我们学习如何在一个进程的地址空间中执行多个线程。

有些情况需要在一个进程中同时执行多个控制流程,这时候线程就派上了场,如实现一个图形界面的下载软件,需要和用户交互,等待和处理用户的鼠标键盘事件,另一方面又需要同时下载多个文件,等待和处理从多个网络主机发来的数据,这些任务都需要一个“ 等待-处理”的循环,可以用多线程实现,一个线程专门 负责与用户交互,另外几个线程每个线程负责和一个网络主机通信。

main 函数和信号处理函数是同一个进程地址空间中的多个控制流程,多线程也是如此,但是比信号处理函数更加灵活,信号处理函数的控制流程只是在信号递达时产生,在处理完信号之后就结束,而多线程的控制流程可以长期并存,操作系统会在各线程之间调度和切换,就像在多个进程之间调度和切换一样。由于同一进程的多个线程共享同一地址空间,因此Text Segment(代码段)、Data Segment(数据段)都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

1. 文件描述符表

2. 每种信号的处理方式(SIG_IGN 、SIG_DFL或者自定义的信号处理函数)

3. 当前工作目录

4. 用户id 和组id

但是有些资源是每个线程各自一份的:

1.线程id(由库函数自己生成 pthread_t( unsigned long long))

2.上下文,包括各种寄存器的值。程序计数器和栈指针

3.栈空间

4.errno变量

5.信号屏蔽字

6.调度优先级

我们将要学习的线程库函数是有POSIX标准定义的,称为POSIXthread或者pthread。 在Linux上 线程函数位于libpthread共享库中,因此编译时要加上 -lpthread选项。

二、线程控制

A.创建线程

3081621aedba066008c14d3a43428c73.png

返回值:成功返回0, 失败返回错误号。以前学过的系统函数都是成功返回0, 失败返回-1,错误号保存在全局变量errno中,pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno, 但这是为了兼容其它函数接口提供的,pthread 库本身并不使用它,通过返回值返回错误码更加清晰。

在一个线程中调pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决 定。start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为

void *, 这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *, 这个指针的含义同样由调用者自己定义。start_routine返回时,这个线程就退出了,其它线程 可以调用pthread_join()得到start_routine的返回值,类似于父进程调用wait(2)得到子进程的退出状态。

pthread_create成功返回后,新创建的线程的id 被填写到thread参数所指向的内存单元。我们知道进程id 的类型是pid_t,每个进程的id在整个系统中是唯一的,调getpid(2) 可以获得当前进程的id,是一个正整数值。线程id 的类型是thread_t, 它只在当前进程中保证是唯一的,在不同的系统中thread_t 这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数printf打印,调用pthread_self(3) 可以获得当前线程的id 。

attr参数表示线程属性,再次不深入讨论线程属性,所有代码例子都传NULL给attr参数,表示线程属性取缺省值。

如下是实例:#include

#include

#include

#include

void* pthread_test(void* arg)

{

int i=0;

while(i++<10)

{

printf("this is thread! pid:%d,tid: %u\n",(int)getpid(),(unsigned long)pthread_self());

sleep(1);

}

return NULL;

}

pthread_t ptr;

int main()

{

int pthread_ret=pthread_create(&ptr,NULL,pthread_test,NULL);

if(pthread_ret!=0)

{

printf("create pthread error!\n,info is:%s\n",strerror(pthread_ret));

exit(pthread_ret);

}

int i=0;

while(i++<10)

{

printf("this is man thread  pid:%d,tid: %u!\n",(int )getpid(),(unsigned long long)pthread_self());

sleep(1);

}

return 0;

}

运行结果:

a6bb5bffb96eb05105eddcba11a6d293.png

可知在Linux上,thread_t类型是一个地址值, 属于同一进程的多个线程调getpid(2)可以得到相同的进程号,调用pthread_self(3)得到的线程号不相同各。由于pthread_create的错误码不保存在errno中,因此不能直perror(3) 打印错误信息,可以先strerror(3) 把错误码转换成错误信息再打印。

如果任意一个线程调用了exit 或_exit, 则整个进程的所有线程都终止,由于从main函数return也相当于调用exit, 为了防止新创建的线程还没有得到执行就终止,我们在main函数return 之前延 时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新创建的线程执行,下面我们会看到更好的办法。

思考题:主线程在一个全局变量ntid 中保存了新创建的线程的id,如果新创建的线程不调用pthread_self 而是直接打印这个ntid, 能不能达到同样的效果?

解答:通过测试得到 线程创建成功时 新建线程的tid确实被保存在thread 地址 所指向的空间(图 1)。但有时我们为了快捷可能用同一个全局变量ntid 创建多个子进程 这时ntid肯定为最后创建的ntid值(图 2),因此只有pthread_self可以达到目的。(自己拙见)

图 1:

f877af5e96f2795c9afeda7d0c222258.png

图 2:

a3f7239f5a20167e24401116942492f8.png

B. 终止线程

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程函数return。这种方法对主线程不适和,从main函数return 相当于调用exit 。

2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3. 线程可以调pthread_exit终止。

pthread_cancel终止一个线程分同步和异步两种情况,比较复杂,自此不介绍。

下面介绍pthread_exit的和pthread_join的用法。

879eaec97436f7e540118d9f4240f42d.png

原文:http://shaungqiran.blog.51cto.com/10532904/1764764

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值