Linux多线程总结

1、线程的理解linux

一、线程实际上是一个进程的一个执行流。服务器

二、线程是操做系统调度的基本单位,进程是承担分配系统资源的基本单位。ide

三、linux下,一个进程就是一个独占资源的线程,即在这个地址空间仅有一个执行流,linux下的进程为轻量级进程(进程能够理解为是线程,能够理解为linux下均为线程),进程和线程均叫作pcb。函数

四、在一个进程中各线程还共享如下进程资源和环境:spa

1)文件描述符表操作系统

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

3)当前工做目录3d

4)用户id和组id指针

五、在一个进程中各线程各有一份:code

1)线程id

2)上下文,包括各类寄存器的值、程序计数器和栈指针

3)栈空间

4)errno变量

5)信号屏蔽字
6.)调度优先级

  在Linux上线程函数位于libpthread共享库中,所以在编译时要加上-lpthread选项。

2、线程控制

一、建立线程

wKioL1nlox3Cq64TAABZzjru9To871.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参数所指向的内存单元。调用getpid(2)能够得到当前进程的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是惟一的,在不一样的系统中thread_t这个类型有不一样的实现,它多是一个整数值,也多是一个结构体,也多是一个地址,因此不能简单地当成整数用printf打印,调用pthread_self(3)能够得到当前线程的id。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
pthread_t tid;
void* thread_run(void *val)
{
	printf("%s : pid is :%d,tid is : %u\n",
			(char*)val,(int)getpid(),
			(unsigned long long)pthread_self());
	return NULL;
}
int main()
{
	int err = pthread_create(&tid,NULL,thread_run,
			"other thread run");
	if( err != 0 ){
		printf("create thread error!info is :%s\n",
				strerror(err));
	}
	printf("main thread run : pid is :%d,tid is : %u\n",
			(int)getpid(),(unsigned long long)pthread_self());
	sleep(1);
	return 0;
}


运行结果:

wKioL1nlrAvhE9EeAABfoW_MVEo589.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秒,内核也不必定会调度新建立的线程执行。

二、终止线程

若是须要只终止某个线程而不终止整个进程,能够有三种方法:
1) 从线程函数return。这种方法对主线程不适用,从main函数return至关于调用。

2)一个线程能够调用pthread_cancel终止同一进程中的另外一个线程。
3)线程能够调用pthread_exit终止本身。

须要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,由于当其它线程获得这个返回指针时线程函数已经退出了。

三、线程等待

wKioL1nlwOiA5nT6AABIdAGFGQ4567.png

     返回值:成功返回0,失败返回错误号
  调用该函数的线程将挂起等待,直到id为thread的线程终止。 thread线程以不一样的方法终止,经过pthread_join获得的终止状态是不一样的,总结以下:
1)若是thread线程经过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
2)若是thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
3)若是thread线程是本身调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。 若是对thread线程的终止状态不感兴趣,能够传NULL给value_ptr参数。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void *thread1(void *val) 
{
	printf("thread 1 returning...\n");
	return (void*)1;
}
void *thread2(void *val)
{
	printf("thread 2 exiting...\n");
	pthread_exit((void*)2);
}
void *thread3(void *val)
{
	while(1){
		printf("pthread 3 is running,wait for be cancal...\n");
		sleep(1);
	}
	return NULL;
}

int main()
{
	pthread_t tid;
	void *tret;
	pthread_create(&tid,NULL,thread1,NULL);
	pthread_join(tid,&tret);
	printf("thread1 return,thread1 id is:%u,return code is:%d\n",
			(unsigned long)tid,(int)tret);
	pthread_create(&tid,NULL,thread2,NULL);
	pthread_join(tid,&tret);
	printf("thread2 exit,thread2 id is:%u,exit code is:%d\n",
			(unsigned long)tid,(int)tret);
    pthread_create(&tid,NULL,thread3,NULL);
	sleep(4);
	pthread_cancel(tid);
	pthread_join(tid,&tret);
	printf("thread3 return,thread3 id is:%u,cancal code is:%d\n",
			(unsigned long)tid,(int)tret);
	return 0;
}


wKioL1nlwf3xqs-lAADE6CA6Hcc794.png

      可见在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。能够在头文件pthread.h中找到它的定义。

  通常状况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。 可是线程也能够被置为detach状态,这样的线程一旦终止就马上回收它占用的全部资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。 对一个还没有detach的线程调用pthread_join或pthread_detach均可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者若是已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

四、线程分离

  在任何一个时间点上, 线程是可结合的(joinable)或者是分离的(detached) 。 一个可结合的线程可以被其余线程收回其资源和杀死。在被其余线程回收以前,它的存储器资源(例如栈)是不释放的。 相反, 一个分离的线程是不能被其余线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

  默认状况下,线程被建立成可结合的。 为了不存储器泄漏,每一个可结合线程都应该要么被显示地回收,即调用pthread_join;要么经过调用pthread_detach函数被分离。若是一个可结合线程结束运行但没有被join,则它的状态相似于进程中的Zombie Process,即还有一部分资源没有被回收,因此建立线程者应该调用pthread_join来等待线程运行结束,并可获得线程的退出代码,回收其资源。
  因为调用pthread_join后,若是该线程没有运行结束,调用者会被阻塞,在有些状况下咱们并不但愿如此。例如,在Web服务器中当主线程为每一个新来的链接请求建立一个子线程进行处理的时候,主线程并不但愿由于调用pthread_join而阻塞(由于还要继续处理以后到来的链接请求),这时能够在子线程中加入代码:
pthread_detach(pthread_self())
或者父线程调用
pthread_detach(thread_id)(非阻塞,可当即返回)
这将该子线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放全部源。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void *thread(void *val)
{
	pthread_detach(pthread_self());
	printf("%s\n",(char *)val);
	return NULL;
}
int main()
{
	pthread_t tid;
	int tret = pthread_create(&tid,NULL,thread,"thread run...");
	if( tret != 0 ){
		printf("create error!,info:%s\n",strerror(tret));
		return tret;
	}
	int ret = 0;
	sleep(1);
	if( 0 == pthread_join(tid,NULL) ){
		printf("pthread wait success!\n");
		ret = 0;
	}
	else{
		printf("pthread wait failed!\n");
		ret = 1;
	}
	return ret;
}

wKiom1nlz72C1W9TAABGYidXUHI519.png

相关文章
相关标签/搜索