linux中的线程(创建,等待,终止,分离,同步与互斥)

一、进程与线程##

《1》问: 什么是进程?什么是线程?二者有何关系?
答:线程是一个PCB,进程是一个PCB组;进程是一个程序运行的实体,每个进程都有它自己的内存地址段,换句话说,进程时执行中的程序,而程序是由多个代码段的集合,一般加载到内存中,是一个没有生命的实体,只有处理器赋予生命时,它才能成为一个活动的实体,另外,进程是指在系统中正在运行的一个应用程序,主要负责资源的分配和管理,线程在linux下,又被称为轻量级进程(LWP),是系统分配处理器时间资源的基本单元,是程序执行的最小单元;
关系: 多线程的进程,又被称为线程组;一个进程至少包括一个线程,通常将该线程称为主线程,若程序只有一个线程,那么就是程序的本身;一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务。

二、线程

《1》什么是线程?
线程包含了表示进程内执行环境必须的信息,包括标识线程的线程ID,一组寄存器值,栈,调度优先级和策略,信号屏蔽字,errno变量以及线程私有数据;

进程内所有信息对于线程都是共享的,包括执行代码,全局变量和堆内存以及文件描述符;
线程标识:
-就像每个进程有一个进程ID一样,线程也有自己的ID;
-进程ID用pid_t来表示,它是一个unsigned int;
-线程ID用pthread_t来表示,pthread_t 不能把它当作整数处理;
-线程可以通过pthread_self()函数获得自身的线程ID

《2》进程和线程的关系图

《3》线程的优缺点
**【1】优点: **
(1)单线程的进程可以简单的认为只有一个线程的进程;
(2)一个进程在同一时间只做一件事,有了多线程后一个进程同一时间可以做很多事;
(3)每个线程可以处理不同的事物,能充分利⽤用多处理器的可并⾏行数量
(4)无论系统有几个CPU,即使程序运行在单CPU上,多线程也可以使进程并发处理多个事物;
(5)一个线程阻塞并不会影响到另外一个线程;
(6)多线程的进程可以尽可能的利用系统CPU资
(7)创建⼀一个新线程的代价要⽐比创建⼀一个新进程⼩小得多;
(8)与进程之间的切换相⽐比,线程之间的切换需要操作系统做的⼯工作要少很多
(9)线程占⽤用的资源要⽐比进程少很多
【2】缺点:
(1)性能损失
一个很少被外部事件阻塞的计算密集型线程往往⽆无法与共它线程共享同⼀一个处理器。如果计算密集型线程的数量⽐比可⽤用的处理器多,那么可能会有较⼤大的性能损失,这⾥里的性 能损失指的是增加了额外的同步和调度开销,⽽而可⽤用的资源不变。
健壮性降低
编写多线程需要更全⾯面更深⼊入的考虑,在⼀一个多线程程序⾥里,因时间分配上的细微偏差 或者因共享了不该共享的变量⽽而造成不良影响的可能性是很⼤大的,换句话说线程之间是 缺乏保护的。
缺乏访问控制 进程是访问控制的基本粒度,在一个线程中调⽤用某些OS函数会对整个进程造成影响。一个线程崩溃,会导致整个进程都异常终止,一个线程调用某些函数,可能会影响进程;
编程难度提高(时序问题,共享资源等操作)
编写与调试一个多线程程序⽐比单线程程序困难得多

三、创建线程(POSIX线程库)

-在进程中只有一个控制线程;
-程序开始运行时候每个进程只有一个线程,它是以单线程方式启动的,在创建多个线程以前,多线程的进程运行与传统的进程没有什么区别
-gcc在链接的时候,需要增加-lpthread选项
-创建一个线程调用pthread_create函数

 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:设置线程属性,为NULL时代表使用默认属性
 //void*(start_routine)(void*):函数地址,表示线程启动后要执行的函数
 //void*arg传给线程启动函数的参数
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include <pthread.h>

void* rout(void* arg)
{
        
        while(1)
        {       
                printf("i am thread ,%lx\n",pthread_self());
                sleep(1);
        }
}

int main()
{
        pthread_t tid;
        int ret;
        ret = pthread_create(&tid, NULL,rout,NULL);
        while(1)
        {
        printf("i am main , %lx\n",pthread_self());
        sleep(1);
        }
return 0;
}

这里写图片描述

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

void* rout(void* arg)
{
        int count = 0;
        while(1)
        {
                ++count;
                if(count >=3)
                {
                 return ;
                }
                printf("i am thread ,%lx\n",pthread_self());
                sleep(1);
        }
}

int main()
{
        pthread_t tid;
        int ret;
        ret = pthread_create(&tid, NULL,rout,NULL);
        while(1)
        {
        printf("i am main , %lx\n",pthread_self());
        sleep(1);
        }
return 0;
}

这里写图片描述

四、进程ID和线程ID

在linux中,线程又被称为轻量级进程,每一个用户态线程,在内核中,都对应一个调度实体,也拥有自己描述符;在没有线程之前,一个进程对应内核中的一个进程描述符,对应一个ID,引入线程之后,相当于多个线程分工完成一个进程的工作,进程和内核的描述符之间一下子就变成了一对多的状态,POSIX标准要求进程的所有线程调用getpid函数时,能够返回相同的进程ID,故而,就引入了线程组的概念;

struct task_struct
{
	pid_t pid;
	pid_t tpid;
	...
	struct task_struct *group_leader;
	...
	struct list_head thread_group;
	...
};

这里写图片描述

五、线程终止与等待

《1》线程终止
线程终止有三种方式:
《1》return(这种方法对主线程不适用,从main函数return相当于调用exit)
《2》pthread_exit(线程成调用此函数,用来终止自己)

void pthread_exit(void* value_ptr);

《3》pthread_cancel(一个线程可以调用它,从而终止同一进程的另一线程)

int pthread_cancel(pthread_t thread);

《2》线程等待
**线程等待的原因: **
(1)已经退出的线程,其空间没有释放,仍然在进程的地址空间内
(2)创建新的进程不会复用刚才退出线程的地址空间
(3)进程等待函数:

int pthread_join(pthread_t thread, void** value_ptr);
//pthread_t thread:线程ID
//void** value_ptr:捕捉相关线程的return值
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include <pthread.h>
void* thread1(void*arg)
{
	printf("thread1 returning ...\n");
	int* p = (int*)malloc(sizeof(int));
	*p = 1;
	return (void*)p;
}

void* thread2(void*arg)
{
        printf("thread2 exiting ...\n");
        int* p = (int*)malloc(sizeof(int));
        *p = 2 ;
        pthread_exit( (void*)p) ;
}

void* thread3(void*arg)
{
 while(1)
 {
        printf("thread3 is running ...\n");
   	sleep(1);
    }     
   return NULL;
}


int main()
{
	pthread_t tid;
	void* ret;
 	 pthread_create(&tid,NULL,thread1,NULL);
	 pthread_join(tid, &ret);
	printf("thread return ,thread id %x, return code : %d\n",tid,*(int*)ret);
 	free(ret);

//thread 2 exit
         pthread_create(&tid,NULL,thread2,NULL);
         pthread_join(tid, &ret);
        printf("thread return ,thread id %x, return code : %d\n",tid,*(int*)ret);
        free(ret);
//thread 3 cancel by other
	 pthread_create(&tid,NULL,thread3,NULL);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid,&ret);
	if( ret == PTHREAD_CANCELED )
	printf("thread return ,thread id %x,return code: PTHREAD_CANCELED\n",tid);
	else
	printf("thread return ,thread id %x,return code :NULL\n",tid);
return 0;
}

这里写图片描述

六 、分离线程##

《1》函数原型
一般由pthread_create 创建的线程,若其第二个参数为NULL,代表使用其默认属性,是不可分离的,这时,可以调用以下函数,实现分离:

int pthread_detach(pthread_self() );
//pthread_self()待分离的线程名

《2》使用场景:(满足以下两个条件)
【1】主线程无需等待子线程
【2】主线程不关心子线程的返回码

七、线程同步与互斥

详情见本人下一篇博客;

猜你喜欢

转载自blog.csdn.net/dai_wen/article/details/79857091
今日推荐