码迷,mamicode.com
首页 > 编程语言 > 详细

linux 线程创建

时间:2016-04-17 17:59:04      阅读:296      评论:0      收藏:0      [点我收藏+]

标签:linux; 线程创建

一、线程的概念

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

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

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

  1. 文件描述符表  

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

   3. 当前工作目录 

   4. 用户id 和组id

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

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

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

   3.栈空间

   4.errno变量

   5.信号屏蔽字

   6.调度优先级

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

二、线程控制 

 A.创建线程

技术分享

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

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

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参数表示线程属性,再次不深入讨论线程属性,所有代码例子都传NULLattr参数,表示线程属性取缺省值。  

  如下是实例:

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

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;
}

运行结果:

技术分享

   可知在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:

技术分享

 图 2:

技术分享

  B. 终止线程  

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

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

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

   3. 线程可以pthread_exit终止。  

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

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

技术分享 



linux 线程创建

标签:linux; 线程创建

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!