Linux下的线程

Linux下的线程

什么是线程?
首先线程是比进程量级更小的单位,线程是进程内部的执行序列,也就是进程内部的执行分支,一个进程可以有多个执行流,多进程本身也是多个执行流,但是各个进程之间是相互独立的,但是线程之间就不是完全的独立的。

线程和进程
进程是资源竞争的基本单位;线程是程序执行的基本单位
线程共享进程的数据,但是也拥有自己的一部分数据,也就是说:进程里面的多个线程,是在同一个地址空间,每一个线程是有自己私有的一部分数据,比如:①线程id ②一组寄存器 ③栈空间 ④errno ⑤调度优先级 ⑥信号屏蔽字

进程和线程的关系图

    可见,进程和线程是在同一个地址空间运行,因此他们的数据段,代码段都是共享的,如果定义一个
函数,那么在各个线程中都是可以调用的,如果定义一个全局变量,那么在各个线程中也是可以调用
的,除此之外,线程共享进程的以下资源:
①文件描述符表 ②每种信号的处理方式 ③当前工作目录id ④用户id和组id
    Linux下的线程:Linux下没有严格意义的线程,因为Linux为进程维护了一个结构体task_struct,但
是没有为线程维护特有的结构体,Linux下,是让一个进程和其他进程共享一个地址空间,来模拟线程
的实现,所以在Linux下,线程就是多个进程在同一个地址空间运行,在同一个地址空间运行的进程,
也就是PCB所代表的执行流都可以叫做:轻量级进程
线程的优点:
·创建一个线程的代价要比进程的代价小的多
·和进程之间的切换相比,线程之间的切换,操作系统要做的工作小得多
·线程占用的资源要比进程小得多,能够充分运用多处理器的可并行数量
·在等待慢速I/O的同时,程序可以执行其他计算机任务
·I/O密集型应用,为了提高性能,将I/O重叠。线程可等待不同的I/O操作
线程的缺点:
·性能损失  ·健壮性降低  ·缺乏访问控制  ·编程难度提高

线程的控制
POSIX线程库
    与线程有关的函数,构成了一个完整的体系,绝大多数函数都是以"pthread_"开头
    链接这些函数库时,要使用编译命令的"-lpthread"选项

创建线程
原型:
    int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void*(*start_routine)(void*),void* arg)
参数:
    thread:返回线程ID
    attr:设置线程的属性,attr为NULL,表示使用默认的线程属性
    void*(*start_routine)(void*):一个函数指针,线程启动后,要执行的函数,这个函数的返回值是void*,参数是void*类型
    arg:指的是传给线程启动函数的参数
返回值:成功返回1,失败返回错误码
说明:传统的函数是成功返回1,失败返回-1,并且对全局变量errno赋值以指示错误,pthread函数出错的时候,
代码:mythread.c

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


void* mythread(void* arg)
{
	while(1){
		printf("i am thread 1\n\n");
		sleep(1);
	}
}

int main()
{
	pthread_t  threadid;
	int ret = 0;
	if((ret = pthread_create(&threadid, NULL,mythread, NULL) != 0))
	{
		perror("look pthread_create");
		return -1;
	}
	while(1){
		printf("i am mainthread\n");
		sleep(1);
	}
	return 0;
}

以上代码的运行结构不确定,因为在Linux下是用进程来模拟线程的,所以线程的创建和进程类似,那么系统在两个
线程之间的调用顺序是和进程一样的,由调度器自己决定。

现在学习相关的几条指令:
ps aux | grep mythread      查看mythread进程
ps -aL | grep mythread       查看nythread的线程
ps -aL | head -nl                 列出进程的相关信息列表

获取线程ID:pthread_t pthread_self(void)
成功返回0,失败返回对应的错误码

线程等待:int pthread_join(pthread_t thread,void** retval)
参数:thread:等待线程的线程id, 
      retval:用来获取新线程的退出状态(就是在pthread_create里面的第三个参数的函数的返回值)
      成功返回0,失败返回对应的错误码
说明:pthread_join函数是阻塞式等待,如果线程没有进行完成,那么主线程就会一直等待,如果不等待的话,就可能有内存泄漏的问题出现,而且等待可以保证退出顺序
线程等待代码:

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


void* mythread(void* arg)
{
	printf("i am thread 1\n\n");
	sleep(1);
	return (void*)1;
}

int main()
{
	pthread_t  threadid;
	int ret = 0;
	if((ret = pthread_create(&threadid, NULL,mythread, NULL) != 0))
	{
		perror("look pthread_create");
		return -1;
	}
	void* retval ;
	pthread_join(threadid,&retval);
	printf("join new thread success,ret:%d\n",(int)retval);
	return 0;
}
 此时当线程结束的时候,主线程就会打印出线程的返回值,如果线程里面是死循环,那么主线程就会一直在等待。

线程的终止
①void pthread_exit(void* retval),用来终止线程的函数,可以代替return语句:pthread_exit((void*)321)
Tip:exit函数是终止进程的,如果在线程里面调用,整个进程就会终止。
②线程取消:int pthread_cancel(pthread_t thread)
 线程的取消有两种方式,自己把自己取消,自己被别人取消,若自己取消自己,则线程直接退出,主线程得到的退出码是-1,自己取消自己的语句是:pthread_cancel(pthread_self());若被别人取消,则线程返回的返回的退出码是-1.

线程的分离:
      在任何的时间点,线程都是可分离和可结合的,一个可结合的线程能够被其他进程收回其资源和杀死,在被其他线程回收之前,他的储存器资源是不释放的,比如:栈;相反一个分离出来的线程是不能被其他线程回收或者杀死的,他的储存器资源在线程结束的时候,有系统自动释放。
      默认情况下,线程被创建成可结合的,为了避免储存器泄漏,每个可结合的线程都应该被回收,也就是调用pthread_join函数,如果一个可结合的线程,结束运行但是没有被join,那么他的状态就类似于僵尸进程的状态,也就是还有一部分的资源还没有被回收,所以线程的创建者就应该调用pthread_join函数来等待线程的结束,回收其资源,得到其退出状态。
      但是由于调用pthread_join之后,由于该线程没有结束,调用者就会一直在阻塞式的等待,但是主线程并不希望阻塞,这个时候就可以在子线程中加入:pthread_detach(pthread_self()),或者在调用者中加入:pthread_deatch(threadid),来分离子线程,此时调用者就成为了非阻塞式等待,而且子线程到运行结束之后,自动释放其所有资源。






限于编者水平,文章难免有缺漏之处,欢迎指正
 如需转载,请注明出处

猜你喜欢

转载自blog.csdn.net/dy_1024/article/details/79239326
今日推荐