多线程编程-线程控制与属性(结合与分离)

进程在各自独立的地址空间中运行,进程之间共享数据需要用mmap或者进程间通信机制,而当需要在一个进程中同时执行多个控制流程并发执行多个任务时,多线程即为出现。


在linux下,一个进程中的多线程都在进程的地址空间中运行,它的控制流程可以长期并存,操作系统会在各线程之间调度和切换:特点如下

1.线程都在当前进程的地址空间中运行,如图:


创建一个进程需要创建自己的地址空间,创建页表,并且建立虚拟地址与页表与物理内存之间的映射关系等,而每个线程只是执行进程中某一部分代码,在其进程地址空间运行,资源共享,只需要建立简单的PCB描述其相关信息,而无需再分配相关资源,建立地址空间等一系列操作。所以线程(性能高,资源小)

2.线程是系统调度的基本单位(由于CPU只看pcb,无关其为进程或线程,而线程又是进程模拟的(特点5));

3.进程是承担分配系统资源的基本单位(操作系统以进程需求分配资源,线程共享其进程资源);

4.linux下进程称为轻量级进程;

5.linux下无真正意义上线程,都为进程模拟的


由于同一进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一 个全局变量,在各线程中都可以访问到(所以线程间通信较为简单,如直接定义全局变量即可实现),以下为线程所共享的进程资源与环境:

1. 文件描述符表;
2. 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数);
3. 当前工作目录;
4. 用户id和组id。

线程私有的资源:

1. 线程id;

2. 上下文,包括各种寄存器的值、程序计数器和栈指针;
3. 栈空间;
4. errno变量;
5. 信号屏蔽字;
6. 调度优先级。


线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread;在Linux上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项。

线程控制:

1.创建线程:

函数:

int phread_create(phread_t* thread,const pthread_attr_t* attr,void* (*start_voutine)(void*),void* arg);
参数:

thread: 调用此函数后返回的子线程id;

attr: 表示此线程属性(此处默认为null);

start_voutine: 在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给函数指针start_routine决 定。start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义;

arg: start_routine函数接收一个参数,是通pthread_create的arg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。


2.线程等待:若线程属性都为可结合的,则目标线程必须阻塞等待子线程,否则可能造成类似僵尸进程的情况

函数:

int pthread_join(pthread_t thread,void** retval)
参数:

thread:等待目标线程的id;

retval:输出型参数,保存目标线程函数执行完后的退出码,三种情况:

1. 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。
2. 如果thread线程被别的线程调用pthread_cancel异常终掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED==-1。
3. 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数,若不需要thread线程的终止状态,可以传NULL给retval参数。

3.线程终止:由上2可知有三种情况

1. 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit;
2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程;

函数:

int pthreas_cancel(pthread_t thread)
参数:

thread:线程id

3. 线程可以调用pthread_exit终止自己;

函数:

void pthread_exit(void* retval)
参数:

retval:retval是void *类型,和线程函数return返回值的用法一样,其它线程可以调用pthread_join获得这个指针.


当一个线程异常退出时,则会导致进程退出,所有其它线程也会退出,由于线程属于进程,一个线程发生异常,操作系统则会给当前进程发送信号,终止此进程,此进程退出,所有资源被回收,其它线程必定也全部退出,所以多线程下的稳定性较差。


线程属性:

默认创建线程是可结合的(joinable),一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的,所以主线程必须等待子线程回收其状态。

相反,若一个线程变为可分离的,这个线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。所以它此时与主线程关系不大,但当当前进程退出时,该分离线程也会退出,因为虽然其已分离,但其资源依然为当前进程的,进程退出资源回收,其必定也随之退出。

结合线程变为分离线程方法:

函数:

int pthread_detach(pthread_self());   //自己改变自己属性
int pthread_detach(pthread_t thread); //父线程改变其属性
参数:

pthread_self():为一个函数,获取当前线程的id;

当一个线程被置为可分离状态,主线程不需再去等待,此时主线程不予干预此子线程的任何事情。


代码实现:

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

void* thread_func(void* arg)   //子线程执行函数 
{
	pthread_t tid=pthread_self();  //获取当前自己线程的id 

	//pthread_detach(tid);          //自己请求成为分离线程 

	/*while(1)
	{
		sleep(1);
		printf("I am child thread!!!\n");
		printf("thread:tid=%u\n",tid);
	}*/
	
	printf("I am child thread!!!\n");
	printf("thread:tid=%u\n",tid);

	pthread_exit((int*)1);            //1.线程终止,调用函数使当前线程函数的返回值(void*)为(int*)1 
	//return (int*)1;                 //2.直接 return返回 
}
int main()
{
	pthread_t tid;
	if(pthread_create(&tid,NULL,thread_func,NULL)<0)   //创建子线程:参数依次:线程ID,线程属性,
	{                                                  //子线程执行函数地址(函数指针),前一个函数的传参参数 
		perror("pthread_create");
		return -1;
	}
	
	//创建多线程 
	/*pthread_t tid1;
	pthread_t tid2;
	pthread_t tid3;
	pthread_create(&tid1,NULL,thread_func,NULL);
	pthread_create(&tid2,NULL,thread_func,NULL);
	pthread_create(&tid3,NULL,thread_func,NULL);*/

	printf("I am main thread!!!\n");
	printf("main:tid=%u thread:tid=%u\n",pthread_self(),tid);
	
	/*sleep(4);
	if(pthread_cancel(tid)==0)          //取消某一个为tid子线程,将会此子线程执行函数的返回值(void*)变为-1 
	{
		printf("main cancel thread success!!!\n");
	}*/

	sleep(4);
	pthread_detach(tid);       //主线程提出与为tid的子线程分离 :分离之后主线程将不阻塞等待子线程执行完成,只执行自己任务,完成后直接返回 

	void* ret;
	int code=pthread_join(tid,&ret);  //线程等待:参数依次:等待子线程id,子线程所执行的函数的返回值(输出型参数); 
	if(code==0)                       //返回值成功为0,失败错误码 
	{
		printf("wait success:\n");
		printf("code=%d:%s  ret=%d\n",code,strerror(code),ret);
	}
	
	/*void* ret1;
	void* ret2;
	void* ret3;
	pthread_join(tid1,&ret1);
	pthread_join(tid2,&ret2);
	pthread_join(tid3,&ret3);*/

	return 0;
}
运行结果如下:

1.当创建线程,终止并等待后结果:



2.当子线程函数无限循环时,主线程调用pthread_cancel()函数时,结果:


当没调用分离函数时主线程都等待成功后退出,当分别调用两个分离函数后,结果:


主线程sleep(4)秒后退出,由于此时在main函数中,所以退出后当前进程退出,所以子线程中无限循环也随之退出。

4.当在主线程下创建多个(此处为4个)线程,并都执行同一个线程函数(有无线循环),结果如下:


查看当前进程中线程如下:




其中LWP代表轻量级线程,操作系统以此为调度单位,并且它们都在同一个进程下运行,PID都为3485.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章