1.线程sleep()后,会让出cpu的时间片,交由其他线程进行抢占cpu。
线程之间正常的切换是依靠时间片的。
当主线程没有结束,且其在所占有的时间片内,并没有结束自己的工作,此时,子线程将会抢占cpu时间片。
参考帖子以及下面的评论:https://blog.csdn.net/harry_lyc/article/details/6055734#reply
1.线程sleep()后,会让出cpu的时间片,交由其他线程进行抢占cpu。
线程之间正常的切换是依靠时间片的。
当主线程没有结束,且其在所占有的时间片内,并没有结束自己的工作,此时,子线程将会抢占cpu时间片。
参考帖子以及下面的评论:https://blog.csdn.net/harry_lyc/article/details/6055734#reply
转载于:https://www.cnblogs.com/xuelisheng/p/9277401.html
由于Linux对线程实现的一些特殊性,所以会给我们理解线程及其相关的一些函数带来疑惑,这里做个解答:
1、关于sleep
很多书籍上都说sleep这个系统调用会使整个进程sleep,而不单是sleep一个线程。这样说没错,但我们要是在Linux下写一个多线程 的程序,在一个线程中调用sleep,你会发现其他线程照样运行,这是怎么回事呢?这就是由于Linux下的线程,其实线程的注册、销毁等工作是在 pthread库里面,而在内核中,线程其实就是一个进程。Linux对线程的实现是通过__clone调用实现的,这个调用会new多个进程出来,只不 过的是, Linux会设置这些进程的共享内存空间、文件描述符等属性为相同,这样就实现了线程的定义(共享一个进程的内存空间、线程切换不需要切换上下文环 境......)。所以,很多书籍上会说Linux的线程在核内是轻量级的进程,就是这个道理。因为和普通进程相比,Linux的线程在核内就是一些被设 置了很多属性的进程,从而实现了线程。
所以,在Linux下,一个线程调用了sleep,反映到核内就是sleep了一个轻量级进程,自然其他的线程(核内的轻量级进程)不受影响,照 样可以run了。简言之,之所以sleep一个线程,其他线程仍然可以执行,这是因为在Linux下,线程和进程不是n对1的,而是1对1的。
2、有关线程的调度
有了1的描述,我们就清楚的知道了,其实Linux对线程的调度,策略和进程是基本一致的。所以,很多人问,为什么一个线程中如果有一个循环的 话,其他线程都得不到执行,这就是因为Linux对线程的调度,策略和进程是一样的,即,如果一个线程处于一个高密度的工作状态的话,或者该线程的cpu slice没用完的话,这个线程是不会被调度的。有关这个问题,还可以参看本论坛中“有关线程切换的一个问题”一帖。
其实说白了,明白了第一点的话,很多Linux线程的问题就迎刃而解了。
有关Linux下线程的实现方式,再附上一段“POSIX线程编程指南”(来自IBM Developworks)中的描述,这段描述很专业,很到位:
我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和 fork (),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因 此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文 件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统 调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所 有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由 __clone()传入。
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外 pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为 线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。
Linux 线程调度与优先级
Linux内核的三种调度策略
1,SCHED_OTHER:分时调度策略。
它是默认的线程分时调度策略,所有的线程的优先级别都是0,线程的调度是通过分时来完成的。简单地说,如果系统使用这种调度策略,程序将无法设置线程的优先级。请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。
2,SCHED_FIFO:实时调度策略,先到先服务。一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。它意味着,使用SCHED_FIFO的可运行线程将一直抢占使用SCHED_OTHER的运行线程J。此外SCHED_FIFO是一个非分时的简单调度策略,当一个线程变成可运行状态,它将被追加到对应优先级队列的尾部。当所有高优先级的线程终止或者阻塞时,它将被运行。对于相同优先级别的线程,按照简单的先进先运行的规则运行。我们考虑一种很坏的情况,如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用SCHED_FIFO的时候要小心处理相同级别线程的动作。
3,SCHED_RR:实时调度策略,时间片轮转。当进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。
鉴于SCHED_FIFO调度策略的一些缺点,SCHED_RR对SCHED_FIFO做出了一些增强功能。从实质上看,它还是SCHED_FIFO调用策略。它使用最大运行时间来限制当前进程的运行,当运行时间大于等于最大运行时间的时候,当前线程将被切换并放置于相同优先级队列的最后。这样做的好处是其他具有相同级别的线程能在“自私“线程下执行。
系统创建线程时,默认的线程是SCHED_OTHER。所以如果我们要改变线程的调度策略的话,可以通过下面的这个函数实现:
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);Linux线程优先级设置
首先,可以通过以下两个函数来获得线程可以设置的最高和最低优先级,函数中的策略即上述三种策略的宏定义:
int sched_get_priority_max(int policy); int sched_get_priority_min(int policy);SCHED_OTHER是不支持优先级使用的,而SCHED_FIFO和SCHED_RR支持优先级的使用,他们分别为1和99,数值越大优先级越高。
设置和获取优先级通过以下两个函数:
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);上面的param使用了下面的这个数据结构:
struct sched_param { int __sched_priority; //所要设定的线程优先级 };例如:
param.sched_priority = 51; //设置优先级测试程序
#include <stdio.h> #include <pthread.h> #include <sched.h> #include <assert.h> //打印当前的线程调度策略 static int get_thread_policy(pthread_attr_t *attr) { int policy; int rs = pthread_attr_getschedpolicy(attr,&policy); assert(rs==0); switch(policy) { case SCHED_FIFO: printf("policy= SCHED_FIFO\n"); break; case SCHED_RR: printf("policy= SCHED_RR\n"); break; case SCHED_OTHER: printf("policy=SCHED_OTHER\n"); break; default: printf("policy=UNKNOWN\n"); break; } return policy; } //打印当前调度策略线程的最高和最低优先级 static void show_thread_priority(pthread_attr_t *attr,int policy) { int priority = sched_get_priority_max(policy); assert(priority!=-1); printf("max_priority=%d\n",priority); priority= sched_get_priority_min(policy); assert(priority!=-1); printf("min_priority=%d\n",priority); } //打印当前线程的优先级 static int get_thread_priority(pthread_attr_t *attr) { struct sched_param param; int rs = pthread_attr_getschedparam(attr,¶m); assert(rs==0); printf("priority=%d\n",param.__sched_priority); return param.__sched_priority; } //设置线程线程的调度策略 static void set_thread_policy(pthread_attr_t *attr,int policy) { int rs = pthread_attr_setschedpolicy(attr,policy); assert(rs==0); get_thread_policy(attr); } int main(void) { pthread_attr_t attr; struct sched_param sched; int rs; rs = pthread_attr_init(&attr); assert(rs==0); //获取默认的线程调度策略 printf("== Show current configuration of priority ==\n"); int policy = get_thread_policy(&attr); show_thread_priority(&attr,policy); //打印当前线程的优先级 printf("show priority of current thread\n"); int priority = get_thread_priority(&attr); printf("== Show different Scheduling method priority ==\n"); //获取SCHED_FIFO调度策略的最高和最低优先级 printf("show SCHED_FIFO of priority:\n"); show_thread_priority(&attr,SCHED_FIFO); //获取SCHED_RR调度策略的最高和最低优先级 printf("show SCHED_RR of priority:\n"); show_thread_priority(&attr,SCHED_RR); printf("== Set thread policy == \n"); //设置线程的调度属性为SCHED_FIFO printf("set SCHED_FIFO policy:\n"); set_thread_policy(&attr,SCHED_FIFO); priority = get_thread_priority(&attr); sched.__sched_priority = 99; pthread_attr_setschedparam(&attr,&sched); priority = get_thread_priority(&attr); //设置线程的调度属性为SCHED_FIFO printf("set SCHED_RR policy:\n"); set_thread_policy(&attr,SCHED_RR); priority = get_thread_priority(&attr); sched.__sched_priority = 55; pthread_attr_setschedparam(&attr,&sched); priority = get_thread_priority(&attr); //恢复线程的调度策略 printf("Restore current policy:\n"); set_thread_policy(&attr,policy); rs = pthread_attr_destroy(&attr); assert(rs==0); return 0; }
Linux 线程调度方式
在创建线程的时候,我们可以指定所要创建线程的属性,属性中有线程的“调度方式”,线程的调度方式有三种:
SCHED_OTHER:分时调度策略
根据nice指来确定线程的运行的顺序(counter+20-nice 值越大,如果当前没有任务在运行,则从就绪队列里找一个这个值最大的线程运行)。nice越小,counter越大。当时间片用完之后或者线程主动交出cpu调度程序才会运行。然后把这个任务放到就绪队列的尾部或者等待队列。重新在就绪队列里找一个(counter+20-nice)最大的线程运行。分时调度策略里是没有优先级的。只有一个类似优先级的概念是(counter+20-nice)。没有优先级,所以没有抢占。
SCHED_FIFO:实时调度策略
先到先服务。一旦占用cpu则一直运行直到有更高优先级任务到达或者自己主动放弃。任务结束后也是放到就绪任务队列的尾部,为了保持相同优先级的任务公平调度。
SCHED_RR实时调度策略:
时间片轮转。从就绪队列里找优先级最高任务执行,当进程时间片用完或者线程主动交出cpu,调度程序才会运行,并置于就绪队列尾或者等待队列中。然后系统在就绪队列里找优先级最高的任务执行。放在对列尾保证了所有具有相同优先级的RR任务的调度公平。
比较SCHED_FIFO 和 SCHED_RR两种实时调度策略:
两者唯一的区别在于采取中两种调度策略的线程最大占用cpu时间的长度不同。
采取SCHED_FIFO调度策略的正在运行的线程释放cpu的情况有:
可见,采取SCHED_FIFO调度策略的线程可能会一直占用着cpu。
采取SCHED_RR调度策略的正在运行的线程释放cpu的情况有:
可见,采取SCHED_RR调度策略的线程占用cpu的时间不会超过分配给它的时间片长度。