第七章 线程操作——Linux C

1.线程概述
(1)线程的基本概念

在一个进程中的多个执行路线叫做线程,更准确的定义是:线程是进程内部的一个控制序列。每个进程至少有一个执行线程。

(2)线程和进程

①说明

●进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,一个拥有资源的独立单元,进程是系统进行资源分配和调度的一个独立单位。
●线程是使用资源的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
 线程只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其它线程共享进程所拥有的全部资源,
 如共享内存、全局变量、文件描述符。
●线程又称为轻量级进程。
●线程是计算机中独立运行的最小单位。进程是分配资源的单位。

②单线程与多线程
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
单线程就是程序中只有单一的顺序控制流程,多线程就是一个程序中同时运行多个线程,完成不同工作 ,执行不同的程序片段。
③区别

●进程和线程的主要差别在于它们是操作系统不同的资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。
●线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,

注:多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
	但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。 			

④进程特点

●系统中程序执行和资源分配的基本单位;
●每个进程有自己的数据段、代码段和堆栈段;
●在进程切换时需要有比较复杂的上下文切换;
●程序健壮性好。

⑤线程的优缺点

▲优点:
	●上下文切换快
	●共享数据容易
	●创建线程速度快
▲缺点:
	●共享内存会导致互相干扰
	●一个线程崩溃会导致整个进程崩溃
(3)用户态线程

用户态: 只能受限的访问内存, 且不允许访问外围设备。占用CPU的能力被剥夺, CPU资源可以被其他程序获取;
用户态线程:不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。
不需要用户态/核心态切换,速度快,操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。
由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。

(4)内核态线程

内核态:CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡。CPU也可以将自己从一个程序切换到另一个程序;
内核级线程:由操作系统内核创建和撤销。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。
创建等操作要进出内核,速度会慢些;
目前流行的操作系统都支持内核级线程。

(5)多线程模型

内核级线程和用户级线程的对应关系

一个内核级线程对应n个用户级线程(用户级线程)
一个内核级线程对应一个用户级线程(内核级线程)
(6)线程池

在程序设计中,并不是一个任务/请求就创建一个线程,执行完毕线程退出。
程序初始化时创建一个线程池,线程池中有n个线程。
当一个请求来时,从线程池中取出一个线程,完成请求后线程再放回线程池。
节省了频繁创建线程和销毁线程的开销。

2.线程管理
注:pthread数据类型
		数据类型				描述
		pthread_t				线程ID
		pthread_mutex_t			互斥对象
		pthread_mutexattr_t		互斥属性对象
		pthread_cond_t			条件变量
		pthread_condaddr_t		条件变量的属性对象
		pthread_key_t			线程特有数据的键
		pthread_once_t			一次性初始化控制上下文
		pthread_attr_t			线程的属性对象   [详见附录1(附录1.txt)]	
	POSIX线程库接口:
		POSIX函数				函数功能描述
		pthread_create			创建一个线程
		pthread_exit			退出线程
		pthread_self			获取线程ID
		pthread_equal			检查两个线程ID是否相等
		pthread_join			等待线程退出
		pthread_detach			设置线程状态为分离状态
		pthread_cancel			线程的取消
		pthread_cleanup_push	线程退出,清理函数注册和执行
		pthread_cleanup_pop		同上			
	pthread_t:
		●线程具有一个ID、一个堆栈、一个执行优先权,以及执行的开始地址;
		●线程的引用使用pthread_t类型的ID来引用, pthread_t是无符号长整数;
		●进程的线程共享进程的完整地址空间,能修改全局变量,访问打开的文件描述符。
(1)创建线程和结束线程

pthread_create创建一个线程,这个线程与创建它的线程同步执行。
在创建线程后,可以调用pthread_self函数得到线程的ID。
线程调用pthread_exit退出。

①线程创建

#include<pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr, void*(*start_routine)(void*), void *arg);
	pthread_create用来创建一个线程。成功返回0,失败返回非0。
	参数说明:
		●thread指向的内存单元将被设置为新创建的线程ID;
		●attr是要创建线程的属性(NULL为默认属性,表示缺省属性);
		●start_routine为线程开始执行的函数;
		●arg为start_routine的参数。
	创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。 
	注: 
		线程共享全局变量,在一个线程中的改变对另一个线程可见。
		arg不能是局部变量,否则线程使用该变量时可能已经被修改或者不存在了。
		start_routine的参数和返回值都为void*,如果有多个参数,那只能打包成一个结构体,返回值不能是局部变量。
		主线程退出,则整个进程结束。

②线程终止
如果进程中的任意线程调用exit(int status)、_exit (int status),那么整个进程都会被终止。
停止单个线程的控制流有三种方式:

●线程从启动函数中返回,返回值是线程的退出码。
●线程可以被其他线程取消。
●线程调用pthread_exit退出。

线程结束两种方法:

pthread_exit:
	#include<pthread.h>
	void pthread_exit(void *retval);//线程退出
		retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join来检索获取。
		调用pthread_exit后,线程终止。
pthread_join:
	见下面(2)的介绍。

③其他

#include<pthread.h>
pthread_t pthread_self (void);
	pthread_self返回调用线程的线程ID;
int pthread_equal (pthread_t t1,pthread_t t2);
	比较两个线程是否是同一个线程。	
(2)挂起线程
int pthread_join(pthread_t th, void**thread_return)
	挂起当前线程,等待指定的线程终止。
	如果thread_return为NULL,则不获取线程的终止状态;挂起的线程直到某种条件得到满足(条件变量的各种函数)。
	参数说明:
		第一个参数指定某个线程;
		第二个参数回收线程的退出值,如果不为NULL,则thread_return=retval。
	执行成功返回0,失败返回非0.
	注意:一个线程仅允许唯一个另一个线程使用pthread_join等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED(分离)状态。
(3)线程同步

当多个线程共享相同的内存时,就需要确保每个线程看到一致的数据。如果数据是只读的,那么不存在一致性问题。
如果线程对数据有读有写,这时候就需要同步机制来保证数据的一致性。 Linux系统中,主要通过互斥量、条件变量和信号量实现线程同步。
互斥量可以帮助多线程同时使用共享资源,以防止一线程试图访问一共享变量时,另一线程正在对其进行修改的情况发生;条件变量则是对互斥量不足的不缺,允许线程互相通知共享变量的状态发生了改变;信号量在互斥的基础上,通过其他机制实现访问者对资源的有序访问。
①互斥量
互斥量用来保证一段时间内只有一个线程在执行一段代码。互斥量其实就是一种锁,在访问共享数据之前设置互斥锁,访问完之后释放这个互斥锁。
互斥量一旦被加锁,其它任何线程再也不能在这个互斥量上加锁,需等到释放锁。

//互斥初始化
静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
			 第一个参数接收该函数创建的互斥锁ID;第二个参数为互斥锁属性,可以为NULL。
			 成功返回0,失败返回非0.
	

//销毁互斥 
int pthread_mutex_destroy(pthread_mutex_t *mutex);
	成功返回0,失败返回非0.


//锁定互斥
int pthread_mutex_lock(pthread_mutex_t *mutex);
	给互斥量mutex加锁。
	如果此互斥量已经加锁,那么调用该函数的线程会被阻塞,如果互斥量没有加锁,调用线程给该互斥量加锁。
	参数mutex是由init函数创建或者静态初始化。
	成功返回0,失败返回非0int pthread_mutex_trylock(pthread_mutex_t *mutex);
	作用:尝试加锁,看是否能加上,是非阻塞版本的加锁。
	如果锁能加上则返回0,不能加上errno设置成EBUSY,错误返回其它值。
	参数mutex跟lock函数一样。
	线程可以用trylock去检查某个mutex是否加锁。如果加锁了,它就去执行其它任务,以此增加并发性。


//解锁互斥
int thread_mutex_unlock(pthread_mutex_t *mutex);
	释放互斥量,成功返回0,失败返回非0。
	必须是加锁的线程才能给互斥量解锁。
	只能对加锁后的互斥量解锁,否则出错。
	注:死锁问题见第六章

②条件变量
多个线程可以等待同一个条件,条件满足时被唤醒。可以唤醒一个线程,也可以唤醒所有等待的线程。
条件变量跟互斥量mutex一起使用。条件变量的类型是pthread_cond_t。
使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。
一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。
这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量用来进行线程间的同步。

//初始化条件变量
静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态初始化:int pthread_cond_init(pthread_cond_t *cond, const pthread_cond_attr *attr);
			动态的初始化条件变量,第二个参数在Linux系统中不起作用,只简单的设为PTHREAD_COND_INITIALIZER,也可为NULL,使用默认属性;
			成功返回0,失败返回其余值。 
	

//销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
	释放由参数指定的条件变量。只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。
	成功返回0,失败返回其余值。 

	
//等待条件变量
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
	第一个参数为初始化的条件变量,第二个参数为初始化的互斥量(默认值是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用)。
	首先将调用线程放入等待条件变量的队列,然后释放mutex,直到条件满足时被唤醒,唤醒从函数返回时再对mutex加锁。

int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);
	与pthread_cond_wait的用法相似,区别在于pthread_cond_timedwait在经过由参数abstime指定的时间后不阻塞。
	函数要求传入的时间值是一个绝对值,不是相对值,例如,想要等待3分钟,必须先获得当前时间,然后加上3分钟。
	
	注:typedef struct timespec{
			time_t tv_sec;			
			long tv_nsex;
		}timespec_t;

	
//激活条件变量
int pthread_cond_signal(pthread_cond_t *cond);					
int pthread_cond_broadcast(pthread_cond_t *cond);
	唤醒等待在条件变量上的线程,如果没有线程等待在条件变量上,这个唤醒动作是不会被保留的。
	函数pthread_cond_t_signal只能唤醒其中一个线程,pthread_cond_broadcast唤醒所有线程。

③信号量

//信号量初始化。
int sem_init (sem_t *sem , int pshared, unsigned int value);
	初始化一个无名信号量,pshared为1时,该信号量可以在进程间使用,为0时只能在当前进程中使用。value信号灯的初值。
	成功返回0,失败返回非0//销毁信号量。
int sem_destroy(sem_t *sem);
	释放由sem_init初始化后的信号量。释放时候必须确保没有任何线程或者进程在使用此信号量,否则程序出现未定义行为。
	成功返回0,失败返回非0//等待信号量。
int sem_wait(sem_t *sem);
	该函数是原子操作,把信号量的值减去1。如果此时信号量的值已经是0,那么调用sem_wait的线程会被阻塞,直到信号量的值变为正数。
	成功返回0,失败返回-1int sem_trywait(sem_t *sem);
	检查信号量的值是否大于0,如果信号量等于0,调用线程并不阻塞,errno设置为EAGAIN。
	成功返回0,失败返回-1int sem_timedwait(sem_t *sem,const struct timespec*sbstime);
	增加了阻塞的时间限制,其余和sem_wait一样,时间到时errno设置为ETIMEDOUT。
	成功返回0,失败返回-1//释放信号量。
	int sem_post(sem_t *sem);
				
注:
	●互斥与同步的区别:
		互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
		同步:主要是流程上的概念,是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。
				在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。				
	
	●互斥锁、条件变量和信号量的区别:
		互斥锁:互斥,一个线程占用了某个资源,那么其它的线程就无法访问,直到这个线程解锁,其它线程才可以访问。
		条件变量:同步,一个线程完成了某一个动作就通过条件变量发送信号告诉别的线程,别的线程再进行某些动作。条件变量必须和互斥锁配合使用。
		信号量:同步,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。而且信号量有一个更加强大的功能,
			信号量可以用作为资源计数器,把信号量的值初始化为某个资源当前可用的数量,使用一个之后递减,归还一个之后递增。			
	
	●另外还有以下几点需要注意:
		①信号量可以模拟条件变量,因为条件变量和互斥量配合使用,相当于信号量模拟条件变量和互斥量的组合。
		 在生产者消费者线程池中,生产者生产数据后就会发送一个信号 pthread_cond_signal通知消费者线程,
		 消费者线程通过pthread_cond_wait等待到了信号就可以继续执行。这是用条件变量和互斥锁实现生产者消费者线程的同步,用信号量一样可以实现!
		②信号量可以模拟互斥量,因为互斥量只能为加锁或解锁(0 or 1),信号量值可以为非负整数,也就是说,
		 一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。
	     当信号量为单值信号量时,就完成一个资源的互斥访问。前面说了,信号量主要用做多线程多任务之间的同步,
	     而同步能够控制线程访问的流程,当信号量为单值时,必须有线程释放,其他线程才能获得,
         同一个时刻只有一个线程在运行(注意,这个运行不一定是访问资源,可能是计算)。如果线程是在访问资源,就相当于实现了对这个资源的互斥访问。
		③互斥锁是为上锁而优化的;条件变量是为等待而优化的; 信号量既可用于上锁,也可用于等待,因此会有更多的开销和更高的复杂性。
		④互斥锁,条件变量都只用于同一个进程的各线程间,而信号量(有名信号量)可用于不同进程间的同步。
		 当信号量用于进程间同步时,要求信号量建立在共享内存区。
		⑤互斥量必须由同一线程获取以及释放,信号量和条件变量则可以由一个线程释放,另一个线程得到。
		⑥信号量的递增和减少会被系统自动记住,系统内部的计数器实现信号量,不必担心丢失,而唤醒一个条件变量时,
		 如果没有相应的线程在等待该条件变量,此次唤醒会被丢失。
(4)取消线程和取消处理程序

①取消线程

int pthread_cancel(pthread_t thread)
	发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。			
int pthread_setcancelstate(int state,int *oldstate)  
	设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
	分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。  			
int pthread_setcanceltype(int type, int *oldtype)  
	设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,
	仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);
	oldtype如果不为NULL则存入运来的取消动作类型值。  		
void pthread_testcancel(void)
	是说pthread_testcancel在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
	线程取消功能处于启用状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。
	如果在取消功能处处于禁用状态下调用pthread_testcancel(),则该函数不起作用。
	pthreads标准指定了几个取消点,其中包括:
		●通过pthread_testcancel调用以编程方式建立线程取消点;
		●线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件;
		●被sigwait(2)阻塞的函数;
		●一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。

②线程终止的清理工作

void pthread_cleanup_push(void (*routine) (void *), void *arg)
	将子程序routine连同它的参数arg一起压入当前线程的cleanup处理程序的堆栈。当当前进程调用pthread_exit或者通过pthread_cancel终止执行时,
	堆栈中的处理程序将按照压栈时相反的顺序依次调用。
	如果pthread_cleanup_push执行失败,则不会返回任何错误报告。
void pthread_cleanup_pop(int execute)
	从线程处理程序中弹出最上面的一个处理程序并执行它。
	如果pthread_cleanup_pop执行失败,则不会返回任何错误报告。
	
	注:
		pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:
			#define pthread_cleanup_push(routine,arg) \ 
			{ 
				struct _pthread_cleanup_buffer _buffer; \ 
				_pthread_cleanup_push (&_buffer, (routine), (arg));
					
			    #define pthread_cleanup_pop(execute) \ 
				_pthread_cleanup_pop (&_buffer, (execute)); \
			}

	可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。
(5)线程特定数据的处理函数

①pthread_key_create函数

#inlcude<pthread.h>
int pthread_key_create(pthread_key_t *key,void(*dest_routine(void *)));
	创建一个进程中的所有线程都可见的关键字。这个关键字可以通过函数pthread_setspecific和pthread_getspecific来读取和设置。
	当创建一个关键字时,进程中的所有线程的这个关键字的值都为NULL,当创建一个线程时,这个线程的所有的关键字的值都为NULL。
	如果执行成功,则返回0,并在参数key中保存新创建的关键字的ID。其他的值意味着错误。

②pthread_key_delete函数

#inlcude<pthread.h>
int pthread_key_delete(pthread_key_t key);
	清楚由参数key指定关键字。
	执行成功返回0,其他的值意味着错误。

③pthread_setspecific函数

#inlcude<pthread.h>
int pthread_setspecific(pthread_key_t key,const void *pointer);
	指定由参数pointer指定的指针由参数key指定关键字。每一个线程都有一个互相独立的指针,这个指针指向意饿特定的关键字。
	执行成功返回0,其他的值意味着错误。

④pthread_getspecific函数

#inlcude<pthread.h>
void* pthread_getspecific(pthread_key_t key);
	用来获取pthread_setspecific设置的关键字指针。
	执行成功则返回一个指向最近一个使用pthread_setspecific而设定的指向线程关键字的指针。
	注意,返回值可能为NULL,此时将不能区分是否为错误。

⑤pthread_once函数

void* pthread_once_t once_control=PTHREAD_ONCE_INT;
int pthread_once(pthread_once_t *once_control,void(*int_routine)(void));			
	函数pthread_once的目的是保证某些初始化代码至多只能执行一次。参数once_control指向静态的或外部的变量,这个变量初始化为PTHREAD_ ONCE INIT。
	当第1次调用ptbread_ once 时,系统将记录已经执行了初始化,后面再调用pthread_ once时,如果参数once_ control 相同,那么就什么也不做。
	pthread_ once 总是返回0
(6)线程属性

①线程属性对象的初始化和销毁函数

#include<pthread.h>
int pthread_attr_init(pthread_attr_t *attr);  //对线程属性对象初始化
int pthread_attr_destroy(pthread_attr_t *attr);  //完成对线程属性对象的销毁
	参数attr为指向线程属性对象的指针。
	调用成功时返回0,失败时返回-1

②线程堆栈大小相关函数

#include<pthread.h>
int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize);  //设置线程堆栈的大小
int pthread_attr_getstacksize(const pthread_attr_t *attr,size_t *stacksize);  //得到线程堆栈的大小
	参数attr是线程属性对象的指针;参数stacksize是堆栈大小或指向堆栈大小的指针。
	调用成功时返回0,失败时返回-1

③线程堆栈地址函数

#include<pthread.h>
int pthread_attr_setstackaddr(pthread_attr_t *attr,void *stackaddr);  //设置线程堆栈的位置
int pthread_attr_getstackaddr(const pthread_attr_t *attr,void **stackaddr);  //得到线程堆栈的位置
	参数attr为指向线程属性对象的指针;参数stackaddr是堆栈地址或指向堆栈地址的指针。
	调用成功时返回0,失败时返回-1

④线程的拆卸状态函数

#include<pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);  //设置线程的拆卸状态
int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate);  //得到线程的拆卸状态
	参数attr为指向线程属性对象的指针;参数detachstate是拆卸状态或指向拆卸状态的指针。
	拆卸状态可能的值是PTHREAD_CREATE_JOINABLE或是PTHREAD_CREATE_DETACHED,默认是前者。
		●在可联合状态中,另外一个线程可以通过pthread_join函数来同步线程的终止,而且可以恢复线程的终止代码,
		 但是有一些线程的资源在线程退出后并不会释放,这样其他线程在创建时可以重新利用这些资源。
		●在脱离状态下,线程的资源在线程结束后立即释放,而且不能用phtread_join函数来同步线程的终止。
	调用成功时返回0,失败时返回-1

⑤线程的作用域函数

#include<pthread.h>
int pthread_attr_setscope(pthread_attr_t *attr,int scope);  //设置线程的作用域
int pthread_attr_getscope(const pthread_attr_t *attr,int *scope);  //得到线程的作用域
	参数attr是指向线程属性对象的指针;参数scope是作用域或指向作用域的指针。
	调用成功时返回0,失败时返回-1

⑥线程的继承调度函数

#include<pthread.h>
int pthread_attr_setinheritsched(pthread_attr_t *attr,int inherit);  //设置线程的继承调度
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int inherit);  //得到线程的继承调度
	参数attr为指向线程属性对象的指针;参数inherit时继承调度或指向继承调度的指针。
	调用成功时返回0,失败时返回-1

⑦线程的调度策略函数

#include<pthread.h>
int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);  //设置线程的调度策略
int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy);  //得到线程的调度策略
	参数attr是指向线程属性对象的指针;参数policy是调度策略或指向调度策略的指针。
	调用成功时返回0,失败时返回-1

⑧线程的调度参数函数

#include<pthread.h>
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);  //设置线程的调度参数		
int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);  //得到线程的调度参数
	参数attr是指向线程属性对象的指针;参数param是sched_param结构或指向该结构的指针。
	如果线程的调度策略是SCHED_OTHER,那么这个参数就可以忽略。只有当线程的调度策略SCHED_RR或者SCHED_FIFO 时,这个参数才有用。
	调用成功时返回0,失败时返回-1

注:

typedef struct				
{      						
	int detachstate;       //线程的分离状态						
	int schedpolicy;    //线程调度策略					
	struct sched_param schedparam;  //线程的调度參数						
	int inheritsched;     //线程的继承性						
	int scope;         //线程的作用域						
	size_t guardsize;     //线程栈末尾的警戒缓冲区大小						
	void* stackaddr;      //线程栈的位置						
	size_t stacksize;        //线程栈的大小					
}pthread_attr_t;

线程属性默认值:

属性 				默认值						说明
contentionscope		PTHREAD_SCOPE_SYSTEM		进程调度相关,NPTL实现,线程只支持在操作系统范围内竞争CPU资源
detachstate			PTHREAD_CREATE_JOINABLE		可分离状态
stackaddr			NULL						不指定线程栈的基址,由系统决定栈基址
stacksize			8196(KB)					默认线程大小为8MB(ulimit -s查看)
guardsize			PAGESIZE					警戒缓冲区
priority			0							进程调度相关,优先级为0
policy				SCHED_OTHER					进程调度相关,调度策略为SCHED_OTHER
inheritsched		PTHREAD_INHERIT_SCHED		进程调度相关,继承启动进程的调度策略
(1)线程分离状态:detachstate
该属性决定了线程运行任务后以什么方式来结束自己。					
方式如下:				
	●PTHREAD_CREATE_DETACHED    ——    分离线程					
		置为分离线程的线程。当不须要被不论什么线程等待,线程运行完任务后,自己主动结束线程,并释放资源。				
	●PTHREAD_CREATE_JOINABLE(缺省)  ——    可汇合线程							
		可汇合线程为线程的默认状态,这样的情况下,原有的线程等待创建的线程结束。仅仅有当pthread_join()函数返回时。
		创建的线程才算终止。才能释放自己占用的系统资源。								
(2)线程的调度策略:schedpolicy
●SCHED_FIFO(先进先出策略)						
	FIFO线程持续执行,直至有更高优先级的线程就绪,或者线程本身进入堵塞状态。							
	当FIFO线程堵塞时,系统将其移出就绪队列,恢复后再增加到同优先级就绪队列的末尾。当FIFO线程被高优先级线程抢占时。
	它在就绪队列中的位置不变。因此一旦高优先级线程终止或堵塞,被抢占的FIFO线程会马上继续执行																		
●SCHED_RR(轮转策略)						
	每一个RR线程会获得一个时间片,一旦RR线程的时间片耗尽,系统即将移到就绪队列的末尾。																		
●SCHED_OTHER(缺省)						
	静态优先级为0。不论什么就绪的FIFO线程或RR线程,都会抢占此类线程。										
(3)调度參数 : sched_param schedparam
结构sched_param在文件/usr/include/bits/sched.h中定义例如以下:					
	struct sched_param							
	{						
		intsched_priority;							
	};					
	结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值相应高的优先权。					
	系统支持的最大和最小优先权值能够用sched_get_priority_max函数和sched_get_priority_min函数分别得到。					
注意:假设不是编写实时程序,不建议改动线程的优先级。由于,调度策略是一件很复杂的事情,假设不对使用会导致程序错误,从而导致死锁等问题。					
	例如,在多线程应用程序中为线程设置不同的优先级别,有可能由于共享资源而导致优先级倒置。
(4)线程的继承性: inheritsched
●PTHREAD_INHERIT_SCHED(缺省) —— 调度属性自创建者线程继承						
●PTHREAD_EXPLICIT_SCHED —— 调度属性由调度參数和调度策略决定					
继承性决定调度的參数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。
pthreads不为inheritsched指定默认值,因此假设你关心线程的调度策略和參数,必须先设置该属性。															
(5)线程的作用域:scope
线程的竞争范围。					
●PTHREAD_SCOPE_SYSTEM ——在系统范围内竞争资源。						
●PTHREAD_SCOPE_PROCESS(Linux不支持)——在进程范围内竞争资源					
(6)线程栈末尾的警戒缓冲区大小:guardsize
该属指定线程末尾的警戒缓冲区大小,在缺省的情况下为一个内存页(4096字节)				
(7)线程栈的位置:stackaddr
(8)线程栈的大小:stacksize
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值