线程调度策略

        对于一个嵌入式多任务、多线程操作系统,所启动的应用进程至少拥有一个线程或多个线程,线程在进程中执行代码。一个进程能够“同时”运行多个线程,“同时”加上引号,因为实际上,在单处理CPU平台上,任何时刻,只有一个线程在执行。操作系统通过任务调度算法快速切换线程来模拟多线程并行,交替地停止一个线程,然后切换到另外一个上运行。支持任务优先级,高优先级线程比低优先级线程更先执行,也就是说低优先线程必须等到高优先级线程被阻塞挂起后才可能被调度。对于优先级别相等的线程使用轮换算法来调度。

 

        无论是WinCE还是Linux操作系统,应用线程的运行总是涉及到两个基本的参数:一个是系统分配给线程的时间片,一个是系统调度的时间间隔。Linux和WinCE下这两个参数有所不同,如下表所示:

 

 

WinCE

嵌入式Linux

  线程的运行时间片

100ms

10ms

  系统调度间隔

1ms

10ms

 

        这里需要注意的是,线程不一定需要将时间片完全用完,事实上,在嵌入式系统中,线程的运行处理时间通常都远小于所分的时间片,这时线程应当调用相关系统函数将自己挂起,系统将立即进行线程重调度。这时的重调度实际是加快了各个线程的轮片,提高了多线程并行运行的程度,客观上保证了嵌入式设备的实时响应能力。

 

        如果线程运行处理的时间超过了系统分配的时间片,在到达时间片后系统将强制挂起该线程,进行任务调度,以保证其他线程的运行。所以在多任务的程序设计中,特别忌讳线程中出现长时间查询代码,如:


while( bFlag == FALSE ) 

        // Polling Flag… 
}


        这种代码总是占完了时间片,大大地浪费CPU资源,使得整个进程的线程调度变得很慢,比如对于WinCE来说,就会出现100ms才调度一次,如果有10个这样的线程,意味着每个线程需要1s才能轮询一次,在嵌入式应用中,就很容易出现数据丢失等错误,从而无法实现正常的功能。对于Linux也是存在同样的问题。

 

        在实际应用中,常用的挂起线程的方法是调用延时函数。对于WinCE,调用Sleep( ms )函数;对Linux,可以调用select( )函数来挂起当前线程。由于系统的调度间隔不一样,对相同的挂起时间,系统会有不同的处理。比如,设置挂起时间为2ms,在WinCE中,将在挂起2ms之后的1ms后,被系统重新调度;在Linux中,由于调度间隔大于挂起的时间,所以系统将在线程挂起10ms之后才被重新调度,也就是说在Linux下,无论程序中设置的挂起时间2ms还是9ms,该线程实际被挂起的时间总是10ms。


 

线程的调度策略分为3个:SCHED_OTHER,SCHED_FIFO,SCHED_RR。


SCHED_OTHER是非实时分时调度策略,线程优先级为0;


试验结果(linux2.6 Montavista 5.0):每个线程都不能强占其它线程,但是线程都受到时间片的限制,并不是线程不主动退出(包括被阻塞),就会一直占用。


但是在sun公司的 《多线程编程手册》中,其说这种情况 线程会一直占用。


SCHED_FIFO是实时先进先出调度策略,即一当占用CPU,除非自己阻塞或结束或有更高优先级线程,否则会一直运行,线程优先级为1-99;


线程会按不同的优先级来分为不同的队列,同优先级的线程是按FIFO来调度的。


SCHED_RR是实时分时调度策略,其不会一直占用CPU,运行一个时间片后会让出CPU给自己同优先级的线程;其实SCHED_RR与SCHED_FIFO基本是相似的,只是前者会受到时间片的限制,相同优先级的线程,用时间片来调度。而FIFO的话,正在运行的线程,是不会被其他同优先级线程强占的,除非自己主动退出或被阻塞。所以在采用FIFO策略时,千万别使用一直占用的线程(不主动退出,也没有挂起的条件),否则其他同优先级线程永远不会运行了。这种情况最好使用RR策略。


指出:SCHED_OTHER是不支持优先级使用的,而SCHED_FIFO和SCHED_RR支持优先级的使用,他们分别为1和99,数值越大优先级越高。实时调度策略会抢占非实时调度策略,即只要有SCHED_FIFO或SCHED_RR这两个实时调度策略的线程,像SCHED_OTHER的非实时调度策略的线程就不会得到运行,除非所有的实时调度策略的线程阻塞或结束。


 

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);

 

  例如以下代码创建了一个优先级为10的线程:

 

struct sched_param 

      int __sched_priority; //所要设定的线程优先级 
};

 

  例:创建优先级为10的线程

 

pthread_attr_t attr; 
struct sched_param param; 
pthread_attr_init(&attr); 
pthread_attr_setschedpolicy(&attr, SCHED_RR); 
param.sched_priority 10; 
pthread_attr_setschedparam(&attr, ¶m); 
pthread_create(xxx &attr xxx xxx); 
pthread_attr_destroy(&attr);
### 关于异类线程调度策略的实现 异类线程调度策略通常涉及动态调整线程的数量以及分配任务的方式,以优化资源利用率并提高性能。以下是一个基于 Java 的简单示例代码,展示了一种可能的异类线程调度策略: #### 动态线程池管理 通过监控当前的任务负载和系统的运行状态,动态决定是否创建新的线程或回收闲置线程。 ```java import java.util.concurrent.*; import java.util.*; public class DynamicThreadPool { private final int corePoolSize; private final int maxPoolSize; private ThreadPoolExecutor executor; public DynamicThreadPool(int corePoolSize, int maxPoolSize) { this.corePoolSize = corePoolSize; this.maxPoolSize = maxPoolSize; initialize(); } private void initialize() { BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(); ThreadFactory threadFactory = Executors.defaultThreadFactory(); RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, 60L, TimeUnit.SECONDS, workQueue, threadFactory, handler ); } public void submitTask(Runnable task) { try { adjustPoolSize(); // 调整线程池大小 executor.submit(task); } catch (RejectedExecutionException e) { System.out.println("Task rejected due to pool size limits."); } } private synchronized void adjustPoolSize() { if (executor.getActiveCount() >= executor.getMaximumPoolSize()) { return; // 如果活动线程数已达到最大值,则不增加线程 } int currentLoad = calculateCurrentLoad(); if (currentLoad > executor.getCorePoolSize()) { executor.setCorePoolSize(currentLoad); // 增加核心线程数 } } private int calculateCurrentLoad() { // 这里可以根据实际需求设计更复杂的逻辑 // 当前线程池的核心线程数加上待处理任务数作为当前负载 return Math.min(maxPoolSize, executor.getCorePoolSize() + executor.getQueue().size()); } public void shutdown() { executor.shutdown(); } } ``` 此代码展示了如何根据当前任务量动态调整线程池的核心线程数[^5]。`adjustPoolSize()` 方法用于评估当前的工作负载,并相应地修改线程池配置。 --- #### 测试代码 为了验证上述 `DynamicThreadPool` 类的功能,可以使用如下测试代码: ```java public class TestDynamicThreadPool { public static void main(String[] args) throws InterruptedException { DynamicThreadPool dynamicThreadPool = new DynamicThreadPool(2, 10); for (int i = 0; i < 20; i++) { final int taskId = i; Runnable task = () -> { System.out.println("Executing Task " + taskId + " by " + Thread.currentThread().getName()); try { Thread.sleep(1000); // Simulate some processing time } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; dynamicThreadPool.submitTask(task); } dynamicThreadPool.shutdown(); dynamicThreadPool.executor.awaitTermination(1, TimeUnit.MINUTES); } } ``` 在此测试中,模拟了向线程池提交多个任务的过程,并观察线程池是否会根据任务数量自动扩展其规模[^6]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值