精华内容
下载资源
问答
  • Linux进程调度的运行队列

    千次阅读 2017-12-02 16:13:00
    当Linux内核要寻找一个新的进程在CPU上运行时,必须只考虑处于可运行状态的进程,(即在TASK_RUNNING状态的进程),因为扫描整个进程链表是相当低效的,所以引入了可运行状态进程的双向循环链表,也叫运行队列...

    当Linux内核要寻找一个新的进程在CPU上运行时,必须只考虑处于可运行状态的进程,(即在TASK_RUNNING状态的进程),因为扫描整个进程链表是相当低效的,所以引入了可运行状态进程的双向循环链表,也叫运行队列(runqueue)。

    运行队列容纳了系统中所有可以运行的进程,它是一个双向循环队列

    该队列通过task_struct结构中的两个指针run_list链表来维持。队列的标志有两个:一个是“空进程”idle_task、一个是队列的长度。

     

    有两个特殊的进程永远在运行队列中待着:当前进程和空进程。前面我们讨论过,当前进程就是由cureent指针所指向的进程,也就是当前运行着的进 程,但是请注意,current指针在调度过程中(调度程序执行时)是没有意义的,为什么这么说呢?调度前,当前进程正在运行,当出现某种调度时机引发了 进程调度,先前运行着的进程处于什么状态是不可知的,多数情况下处于等待状态,所以这时候current是没有意义的,http://Ubuntuone.cn/ 直到调度程序选定某个进程投入运行后,current才真正指向了当前运行进程;空进程是个比较特殊的进程,只有系统中没有进程可运行时它才会被执 行,Linux将它看作运行队列的头,当调度程序遍历运行队列,是从idle_task开始、至idle_task结束的,在调度程序运行过程中,允许队 列中加入新出现的可运行进程,新出现的可运行进程插入到队尾,这样的好处是不会影响到调度程序所要遍历的队列成员,可见,idle_task是运行队列很 重要的标志。

    另一个重要标志是队列长度,也就是系统中处于可运行状态(TASK_RUNNING)的进程数目,用全局整型变量nr_running表示,在/kernel/fork.c中定义如下:

    int nr_running=1;

    若 nr_running为0,就表示队列中只有空进程。在这里要说明一下:若nr_running为0,则系统中的当前进程和空进程就是同一个进程。但是Linux会充分利用CPU而尽量避免出现这种情况。

    展开全文
  • 适用于: Linux OS - Version Oracle Linux 5.2 to OracleLinux 7.3 [Release OL5U2 to OL7U3] Linux x86-64 Linux x86 ...负载平均值是运行队列(状态R)或等待磁盘I / O(状态D)在1,5和15分钟内

    适用于:

    Linux OS - Version Oracle Linux 5.2 to OracleLinux 7.3 [Release OL5U2 to OL7U3]
    Linux x86-64
    Linux x86

    用途:

    如何理解操作系统负载均衡和运行队列

    解决方案:

    首先让我们了解什么是负载均衡:

    负载平均值是运行队列(状态R)或等待磁盘I / O(状态D)在1,5和15分钟内平均的作业数。

    uptime / top命令的输出示例:

    # uptime
    11:49:14 up 25 days, 5:56, 68 users, load average: 0.03, 0.13, 0.47    <-----

    # top -b -n 1
    top - 11:49:34 up 25 days, 5:56, 68 users, load average: 0.09, 0.14, 0.46  <-----
    Tasks: 456 total, 1 running, 445 sleeping, 10 stopped, 0 zombie
    Cpu(s): 0.8%us, 0.4%sy, 0.0%ni, 98.6%id, 0.2%wa, 0.0%hi, 0.0%si, 0.0%st
    Mem: 141823788k total, 140483388k used, 1340400k free, 313452k buffers
    Swap: 16772092k total, 0k used, 16772092k free, 134695384k cached

    经验法则是:

    单核系统 - 如果负载平均值为1.00,意味着系统被充分利用,如果有更多任务传入,它们将排队等待排除

    单核系统 - 如果负载平均值为2.00,则意味着系统已经被使用,并且一些任务已经排队等待执行

    多核系统(4核) - 如果负载平均为1.00,这意味着系统使用他的CPU能力的1/4,一个任务正在运行,还有3个核处于“空闲”阶段

    多核系统(4核) - 如果负载平均为4.00,这意味着系统使用所有4个核,并且它指示系统被充分利用

    在上述情况下,还有一些顶部空间还留下来吗? - 通常没有 - 如果负载平均值接近系统的核心数量 - 应该检查操作系统,以查找实际瓶颈和失误的调整,或者操作系统未正确扩展以服务任何APP / DB任务。

    如何在活动操作系统上计算负载平均值? - 为此,我们需要通过vmstat命令找到可用的运行队列:

    空闲系统(8核)

    vmstat 1 6
    procs -----------memory---------- ---swap-- -----io---- --system-------cpu-----
    r b swpd free buff cache si so bi bo in cs us sy id wa st
    0 0 0 1674516 316588 134364752 0 0 0 1 0 0 1 0 99 0 0
    0 0 0 1674624 316588 134364752 0 0 0 0 195 307 0 0 100 0 0
    0 0 0 1674624 316596 134364752 0 0 0 12 168 302 0 0 100 0 0
    0 0 0 1674624 316596 134364752 0 0 0 0 198 331 0 0 100 0 0
    0 0 0 1674624 316596 134364752 0 0 0 0 206 356 0 0 100 0 0
    0 0 0 1674624 316600 134364736 0 0 0 12 197 333 0 0 100 0 0

    活动系统(8核)

    vmstat 1 6
    procs -----------memory---------- ---swap-- -----io---- --system-------cpu-----
    r b swpd free buff cache si so bi bo in cs us sy id wa st
    5 0 0 1674516 316588 134364752 0 0 0 1 0 0 1 0 99 0 0
    7 0 0 1674624 316588 134364752 0 0 0 0 195 307 0 0 100 0 0
    2 0 0 1674624 316596 134364752 0 0 0 12 168 302 0 0 100 0 0
    6 0 0 1674624 316596 134364752 0 0 0 0 198 331 0 0 100 0 0
    1 0 0 1674624 316596 134364752 0 0 0 0 206 356 0 0 100 0 0
    8 0 0 1674624 316600 134364736 0 0 0 12 197 333 0 0 100 0 0

    上面的输出是示例 - 第一个示出了当前运行队列(r)为0,其中活动系统运行队列在6个探测中从1跳到8。

     

    什么是运行队列?

    run-queue:活动(正在运行)和排队的进程数。

    在第二个例子中,当系统处于活动状态时,我们看到运行队列为8 - 这已经是最大上限系统,具有8个内核应该运行。

    当然,运行队列可能显示36或甚至101的值 - 如果第一个36有36个核心,第二个101我们有超过101个核心。

    运行队列应始终低于/等于系统上安装的核心数 - 当然运行队列100可以在只有8个核心的系统上可见 - 这将意味着8个进程主动由CPU服务,其余92个进程排队并等待执行。

    如果运行队列高于安装的CPU核心,则应该检查APP / DB性能和缺少的调整,或者可以指示系统未正确扩展以服务此类运行队列/负载。

    就像负载平均运行队列应该保持在安装的核心数以下 - 不保持这个值低于最大阈值将导致减速/挂起或驱逐情况(如果系统是HA启用)作为操作系统可以简单地在磁盘/网络层上排队心跳监控因为其忙于服务其他任务。

    高负载平均和运行队列将导致突然崩溃/挂起的情况 - 它值得通过第三方监控工具主动监控这两个值,并在运行队列/负载平均占用超过70%的实际CPU资源时发出警报。

    Load Average也采用的第二个重要列是vmstat中的'b'状态,它解释了阻塞状态进程- 这可以轻松地解释为状态D进程(等待后端IO完成 - 通常是存储活动)

    如果负载平均值高,并且没有进程正在活动,并且vmstat显示异常的'b'状态值,那么它来检查SAN性能或验证任何操作系统组件的时候,如ISCSI / NFS / NIC / HBA,可能会遇到一些问题,在Linux上导致严重阻塞状态。

    例如,NFS服务器可能在CPU级别上忙,并且所有客户端(Linux)进程/任务将在状态d(b)中排队,导致“排队”,随后可能随后释放大量运行队列,因为所有进程等待后端IO完成后,他们可能再次切换到运行导致大量运行队列,这之后可能导致挂起/恐慌状态或导致驱逐案例。

    要快速列出运行/阻塞的进程,请使用以下ps命令:

    # ps r -Af

    还验证操作系统CPU是否正在服务活动用户(US)空间使用以下命令示例:

    # sar -P ALL 1
    Linux 3.8.13-118.13.3.el6uek.x86_64 (lnx-ovm-san2076.uk.oracle.com) 01/08/2017_x86_64_ (8 CPU)

        02:40:38 PMCPU %user %nice %system %iowait %steal %idle
    02:40:39 PM all 12.62 0.00 0.12 6.88 0.00 80.38
    02:40:39 PM 0 0.00 0.00 0.00 54.55 0.00 45.45
    02:40:39 PM 1 0.00 0.00 0.00 0.00 0.00 100.00
    02:40:39 PM 2 0.99 0.00 0.00 0.00 0.00 99.01
    02:40:39 PM 3 0.00 0.00 0.00 0.00 0.00 100.00
    02:40:39 PM 4 100.00 0.00 0.00 0.00 0.00 0.00
    02:40:39 PM 5 0.98 0.00 0.98 0.00 0.00 98.04
    02:40:39 PM 6 0.00 0.00 0.00 0.00 0.00 100.00
    02:40:39 PM 7 0.00 0.00 0.00 0.00 0.00 100.00

        Average: CPU%user %nice %system %iowait %steal %idle
    Average: all 12.63 0.00 0.13 6.00 0.00 81.24
    Average: 0 0.00 0.00 0.00 45.23 0.00 54.77
    Average: 1 0.50 0.00 0.00 3.00 0.00 96.50
    Average: 2 0.50 0.00 0.00 0.00 0.00 99.50
    Average: 3 0.00 0.00 0.00 0.50 0.00 99.50
    Average: 4 100.00 0.00 0.00 0.00 0.00 0.00
    Average: 5 0.50 0.00 0.50 0.00 0.00 99.00
    Average: 6 0.00 0.00 0.00 0.00 0.00 100.00
    Average: 7 0.00 0.00 0.00 0.00 0.00 100.00

    # mpstat -P ALL
    Linux 3.8.13-118.13.3.el6uek.x86_64 (lnx-ovm-san2076.uk.oracle.com) 01/08/2017_x86_64_ (8 CPU)

        02:41:26 PMCPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle
    02:41:26 PM all 0.79 0.00 0.10 1.18 0.00 0.02 0.00 0.00 97.92
    02:41:26 PM 0 0.94 0.00 0.14 2.84 0.00 0.02 0.00 0.00 96.06
    02:41:26 PM 1 0.94 0.00 0.14 2.70 0.00 0.02 0.00 0.00 96.20
    02:41:26 PM 2 0.93 0.00 0.14 1.13 0.00 0.03 0.00 0.00 97.77
    02:41:26 PM 3 0.94 0.00 0.13 2.71 0.00 0.02 0.00 0.00 96.20
    02:41:26 PM 4 0.65 0.00 0.06 0.01 0.00 0.01 0.00 0.00 99.28
    02:41:26 PM 5 0.65 0.00 0.06 0.01 0.00 0.01 0.00 0.00 99.27
    02:41:26 PM 6 0.65 0.00 0.06 0.01 0.00 0.01 0.00 0.00 99.27
    02:41:26 PM 7 0.64 0.00 0.05 0.01 0.00 0.01 0.00 0.00 99.29

    注意

    默认情况下,oswatcher捕获mpstat/vmstat/top,有关更多详细信息,请检查:1531223.1,此外,OS还会捕获/var/log/sa中的标准SAR数据

    如果系统运行的上限是8个运行的进程,负载平均值在8核系统上相同的值? - 不是

    系统应该正确扩展,并且不超过其可能性的70% - 这对于任何新的任务都有一定的要求 - 这对于HA启用的服务器和具有任何高端IO/网络的系统尤其重要,可能由活动操作系统意外排队。对于这个深入检查应该由APP / DB团队来验证是什么在操作系统下运行。

    只有APP / DB任务导致高运行队列/负载平均? - 不是

    某些操作系统任务可能会导致高运行队列或负载平均 - 但这些都是很少见的情况。

    在这种情况下,top命令将有助于监视US / SY / NI / ID / WA / HI / SI / ST值,并专注于SY(系统)部分,告诉处理器在内核级别花费多少时间。 确保其总是低于实际的US(用户)利用率,并且SY不是使用例如20-30%的CPU(取决于CPU设置和实际情况)。

    例如,高SY可能在高IO /网络操作期间或在内存不足情况期间可见。

     

    展开全文
  • CPU-上下文切换,运行队列和使用率

    千次阅读 2017-12-13 14:18:10
    关于CPU,有3个重要的概念:上下文切换(context switchs),运行队列(Run queue)和使用率(utilization)。   上下文切换:   目前流行的CPU在同一时间内只能运行一个线程,超线程的处理器可以在同一...

    引用 :http://blog.itpub.net/24435147/viewspace-694469/


    关于CPU,有3个重要的概念:上下文切换(context switchs),运行队列(Run queue)和使用率(utilization)。 

      上下文切换: 

      目前流行的CPU在同一时间内只能运行一个线程,超线程的处理器可以在同一时间运行多个线程(包括多核CPU),Linux内核会把多核的处理器当作多个单独的CPU来识别。 

      一个标准的Linux内核可以支持运行50~50000个进程运行,对于普通的CPU,内核会调度和执行这些进程。每个进程都会分到CPU的时间片来运行,当一个进程用完时间片或者被更高优先级的进程抢占后,它会备份到CPU的运行队列中,同时其他进程在CPU上运行。这个进程切换的过程被称作上下文切换。过多的上下文切换会造成系统很大的开销。 

      运行队列 

      每个CPU都会维持一个运行队列,理想情况下,调度器会不断让队列中的进程运行。进程不是处在sleep状态就是run able状态。如果CPU过载,就会出现调度器跟不上系统的要求,导致可运行的进程会填满队列。队列愈大,程序执行时间就愈长。 

      关于时间片和动态优先级 

      时间片对于CPU来说是很关键的参数,如果时间片太长,就会使系统的交互性能变差,用户感觉不到并行。如果太短,又会造成系统频繁的上下文切换,使性能下降。对于IO Bound的系统来讲并不需要太长的时间片,因为系统主要是IO操作;而对于CPU Bound的系统来说需要长的时间片以保持cache的有效性。 

      每一个进程启动的时候系统都会给出一个默认的优先级,但在运行过程中,系统会根据进程的运行状况不断调整优先级,内核会升高或降低进程的优先级(每次增加或降低5),判断标准是根据进程处于sleep状态的时间。 

      IO Bound进程大部分时间在sleep状态,所以内核会调高它的优先级,CPU Bound进程会被内核惩罚降低优先级。因此,如果一个系统上即运行IO Bound进程,又运行CPU Bound进程,会发现,IO Bound进程的性能不会下降,而CPU Bound进程性能会不断下降。 

      经验总结: 

      1. 对于每一个CPU来说运行队列不要超过3,例如,如果是双核CPU就不要超过6; 

      2. 如果CPU在满负荷运行,应该符合下列分布, 

      a) User Time:65%~70% 

      b) System Time:30%~35% 

      c) Idle:0%~5% 

      3. 对于上下文切换要结合CPU使用率来看,如果CPU使用满足上述分布,大量的上下文切换也是可以接受的。





    http://blog.csdn.net/lmy4710/article/details/41863927


    展开全文
  • 模块之后才发生的,那么就需要将各个模块中定义的运行队列都收集在一起,然后才能统一执行。那么又是在什么地方将这些运行任务都收集起来的呢?答案就在loadModules函数的实现中: function loadModules ...

    在上一篇文章中,介绍了constant的生命周期:它是如何被定义的,如何被创建,如何被使用的。本文继续介绍module上更多高层API的实现细节。在继续阅读下面的内容之前,还是建议对依赖注入本身要有足够的理解,当然如果你是跟着依赖注入的这一系列文章一路走来,对angular实现依赖注入的方式和细节应该是比较熟悉了。

    本文会介绍定义与module上的两个方法:module.config以及module.run。表面上看它们好像没有什么区别,一个是配置另一个是运行。实际上,它们的区别还是挺大的。相信开发angular应用的同学们绝对不可能没用过module.config方法吧,因此我们就从它开始讨论。

    配置队列(Config Queue)

    首先,我们来思考思考配置队列这一机制的必要性。现在,我们已经知道依赖注入实际上是由两个注入器协力完成的。我们经常使用的是实例注入器,但是当实例注入器找不到某个对象的时候,还是会去provider注入器那里需求帮助。所以provider注入器目前就是一个被动的角色,平常无人问津,有问题了就会有实例注入器来找它帮忙。如果我们需要直接使用provider注入器中管理的对象,也不是没有办法,可以通过创建一个provider来完成,因为在provider的构造函数中是可以直接将provider注入器中管理的对象注入进来的。但是这样子是不是太麻烦了呢?为了使用其它providers,还需要单独创建一个毫无意义,只为使用它们的provider?这样真的好吗?而且我们知道,provider存在的意义之一也是为了配置,配置应该如何创建出真正的对象。所以应该提供一个地方能够无拘无束地使用providers来进行各种配置工作。在angular中,这个地方就是module上的config方法和它所对应的配置队列。

    下面我们就来看看相关代码:

    var configBlocks = [];
    var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
    moduleInstance.config = config;
    
    // invokeLater函数的定义
    function invokeLater(provider, method, insertMethod, queue) {
      if (!queue) queue = invokeQueue;
      // 还是利用柯里化将多个参数的函数转换为少数参数的函数
      return function() {
        // arguments才是我们在声明constant时实际传入的参数
        queue[insertMethod || 'push']([provider, method, arguments]);
        return moduleInstance;
      };
    }

    以上就是配置队列在module中的相关定义。这里仍然用到了我们的老朋友invokeLater函数。只不过最后显式地将configBlocks队列当作queue传入到了invokeLater函数中。因此,做一些基本的参数替换后,config方法的实际行为如下:

    var config = function() {
      configBlocks['push']('$injector', 'invoke', arguments);
    };

    那么configBlocks队列又是在何时被使用的呢?答案还是在注入器加载模块的那段代码中:

    if (isString(module)) {
      // 和运行队列相关的代码,马上就会介绍
      moduleFn = angularModule(module);
      runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
    
      // 执行任务队列 - 这个我们已经相当熟悉了
      runInvokeQueue(moduleFn._invokeQueue);
    
      // 执行配置队列 - 这个是我们当前重点分析对象
      runInvokeQueue(moduleFn._configBlocks);
    }

    有几个细节需要把握,在执行配置队列中定义的配置任务之前,会执行任务队列。它的目的是保证在进行任何配置之前,将定义的各种服务都注册好。所谓注册,也就是将各种服务的底层provider都准备好。毕竟在配置任务中也是有可能需要使用这些服务对应的provider的,比如下面这段代码但凡用过angular的开发者都写过:

    // 如果你用Angular UI Router比较多,那么下面的$routeProvider就是$urlRouterProvider
    module.config(function('$routeProvider') {
      // ......
    });

    注意在config方法接受的函数中被注入的参数是一个provider对象。这也是config方法的初衷,为了提供一个使用providers进行配置的地方。

    那么具体而言,这个行为是如何实现的呢。其实就是把对于config函数的调用交给了$injector.invoke方法。关键就是这个$injector到底表示的是实例注入器呢,还是provider注入器呢:

    // 对照config的定义:(['$injector', 'invoke', arguments])
    // invokeArgs[0]: '$injector'
    // invokeArgs[1]: 'invoke':
    // invokeArgs[2]: 类数组对象arguments
    function runInvokeQueue(queue) {
      var i, ii;
      for (i = 0, ii = queue.length; i < ii; i++) {
        var invokeArgs = queue[i],
            provider = providerInjector.get(invokeArgs[0]);
    
        provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
      }
    }

    注意看for循环中的这一行代码:

    // 相当于调用的providerInjector.get('$injector')
    provider = providerInjector.get(invokeArgs[0]);

    所以是期望从provider注入器中拿到$injector,而provider注入器中的cache有没有保存这个$injector对象呢?答案是保存了,而且$injector指的就是provider注入器自己:

    providerInjector = (providerCache.$injector =
              createInternalInjector(providerCache, function(serviceName, caller) {
                if (angular.isString(caller)) {
                  path.push(caller);
                }
                throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
              })),

    创建provider注入器之后将它保存在了自身的缓存中。因此对于config中定义的函数会被provider注入器进行调用,所以config函数中定义的依赖全部都来源于provider注入器的缓存。这就是整个config从定义到执行的过程,其实也没什么特别的,关键的概念我们之前几乎都已经接触过了,有的还不止一次。只要了解了注入器的工作原理可谓是轻车熟路。

    除了通过调用module.config来注册一段配置任务之外,angular其实还提供了另一种方法。就是在声明module时传入的第三个参数:

    // 第三个参数configFn就是需要执行的配置函数
    function module(name, requires, configFn) {}
    
    // 在内部同样还是通过调用config方法完成配置函数的注册
    if (configFn) {
      config(configFn);
    }

    使用哪一种方式进行配置函数的注册完全是看开发人员的个人爱好。但是我觉得还是使用module.config来注册配置函数更好,代码的可读性似乎更高一些。

    运行队列(Run Queue)

    module中定义了另外一个名为run的方法。从文档上来看:

    /**
     * @ngdoc method
     * @name angular.Module#run
     * @module ng
     * @param {Function} 在注入器被创建后执行。用于应用的初始化。
     * @description
     *  使用此方法来注册那些注入器加载完成所有模块后需要执行的任务。
     */
    run: function(block) {
      runBlocks.push(block);
      return this;
    }

    好像和config没什么太的区别?都是运行一段代码。还是看看在创建注入器时和运行队列相关的行为:

    // createInjector函数中和运行队列相关的代码
    // loadModules加载模块的函数返回值就是运行队列
    var runBlocks = loadModules(modulesToLoad);
    instanceInjector = protoInstanceInjector.get('$injector');
    // 依次运行每个运行任务
    forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });

    这里有三个值得注意的细节:
    第一个是loadModules函数其实是有返回值的,这个返回值就是运行队列。
    第二个是每个执行队列中的任务都是通过实例注入器来调用的: instanceInjector.invoke(fn)。这一点和前面介绍的配置队列也不太一样,配置队列是通过provider注入器调用。
    第三个是运行队列的执行是在加载完所有模块之后才发生的,这一点和配置队列以及前面介绍的任务队列不太一样,后两者是在加载某个模块时就会发生的。既然运行队列的执行是在加载完所有模块之后才发生的,那么就需要将各个模块中定义的运行队列都收集在一起,然后才能统一执行。那么又是在什么地方将这些运行任务都收集起来的呢?答案就在loadModules函数的实现中:

    function loadModules(modulesToLoad) {
      var runBlocks = [];
      forEach(modulesToLoad, function(module) {
        // ......
    
        try {
          if (isString(module)) {
            moduleFn = angularModule(module);
            // module的递归加载在这里发生,运行队列的收集也在这里进行
            runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
    
            // 执行当前正在被加载模块的任务队列以及配置队列
            runInvokeQueue(moduleFn._invokeQueue);
            runInvokeQueue(moduleFn._configBlocks);
          } else if (isFunction(module)) {
            // 如果模块是一个函数,那么使用provider注入器调用之,并将其返回值放入到执行队列
            runBlocks.push(providerInjector.invoke(module));
          } else if (isArray(module)) {
            // 如果模块是一个数组对象,那么使用provider注入器调用之,并将其返回值放入到执行队列
            runBlocks.push(providerInjector.invoke(module));
          } else {
            assertArgFn(module, 'module');
          }
        } catch (e) {
          // ......
        }
      });
      return runBlocks;
    }

    可见,执行队列的收集工作是伴随着模块的递归加载而完成的。在加载一个模块的时候,只会对执行队列进行收集,而不像之前介绍的任务队列和配置队列那样,加载模块的同时就会调用runInvokeQueue函数来实际地执行它们。

    同时,我们也发现了module除了是字符串类型(也就是模块的名称)之外,还能够是一个函数类型。当被声明为函数类型时,会有单独的处理:使用provider注入器调用,并将其返回值放入到执行队列。这又是在玩哪一出呢?module居然还可以无名无姓,就是一个函数?其实,这种直接以函数的形式定义的module的作用就相当于是一个定义了配置任务和执行任务的综合体。

    来分析比较一下下面两段代码便知:

    // 这是定义在module中的config方法,本质上它还是调用的provider注入器的invoke方法
    var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
    
    // 这是在将module定义为函数时的调用方式,直白地使用了provider注入器的invoke方法
    runBlocks.push(providerInjector.invoke(module));

    它们两者的定义方式虽然不同,但是都有一颗providerInjector.invoke的心脏。所以在这样定义的module中,也是可以将各种providersconstant作为依赖声明为参数的,比如这样:

    // 假设'a'是一个常量,'bProvider'是一个provider
    angular.module(function(a, bProvider){});

    这种方式提供了一个方便快捷地定义配置任务的方案,如果只是希望进行简单的配置工作,完全可以不通过传统的module.config来进行任务的注册。直接使用函数形式的module即可。而且,为了让它更加方便,它的返回值也被利用上了,返回值函数会被当作执行任务投放到runBlocks中,因此下面这种定义方式直接定义了一个config任务和一个run任务:

    // 假设'a'是一个常量,'bProvider'是一个provider, 'cService'是一个service
    angular.module(function(a, bProvider){
      // 一些配置工作
      return function(a, cService) {
        // 一些初始化工作
      };
    });

    和依赖注入相关的三种队列

    至此,我们已经接触到了和依赖注入密切相关的三种队列,下面简单总结回顾一下它们的区别:

    1. 任务队列(Invoke Queue)

    调用module上的高层API就会向任务队列中增加一个相应任务,比如module.constant的调用就导致了一个常量任务的创建,具体而言就是定义在依赖注入模块(injector.js)中的constant函数的执行:

    function constant(name, value) {
      assertNotHasOwnProperty(name, 'constant');
      providerCache[name] = value;
      instanceCache[name] = value;
    }

    除此之外,还有对应于configfactoryservice等方法的定义在注入器中的factory函数和service函数等。对于factoryservice等任务而言,它们的执行并不会导致真正的对象被创建,而是注册一个具体的provider,这个provider知道该如何创建真正的对象,待需要的时候创建之,从而实现”懒加载”。执行时机上,任务队列的执行发生在模块被加载的时候。

    1. 配置队列(Config Queue)

    本文介绍的配置队列是为了提供一个配置各种providers的地方,通过provider注入器完成调用。任务队列和执行队列中定义的任务都是通过实例注入器进行调用的,因此它们无法直接地注入定义在provider注入器中的各种providers。配置队列的执行时机和任务队列相似,都是在加载某个模块的时候就会被执行,但是在顺序上它的执行发生在同模块的任务队列执行之后。

    1. 运行队列(Run Queue)

    它用于定义一些模块的初始化工作。和任务队列一样,定义在运行队列中的任务都是通过实例注入器完成实际的调用工作的。但是和它以及配置队列不一样的是,它的执行时机实在所有模块全部加载完毕之后,此时所有的服务都已经完成注册工作(各路providers都已经准备好,知道如何初始化托管对象)。所以当运行队列中的任务被执行时,它是可以将需要的各种依赖都声明在其参数列表中的,哪怕这些依赖被定义在不同的模块中。


    至此,我们已经了解到了和依赖注入实现息息相关的三种队列结构以及它们各自的特点和用法。在下一篇文章中会继续介绍module中剩下的几个方法,这些方法在我们的实际应用中会经常被用到,比如factoryservice等。

    展开全文
  • 多处理器运行队列的平衡

    千次阅读 2010-04-25 12:22:00
    因此,内核周期性地检查运行队列的工作量是否平衡,并在需要的时候,把一些进程从一个运行队列迁移到另一个运行队列。但是,为了从多处理器系统获得最佳性能,负载平衡算法应该考虑系统中CPU的拓扑结构。从内核2.6.7...
  • Linux系统的运行队列深度可以使用vmstat命令监视
  • Laravel 队列系统实现及使用教程

    千次阅读 2021-01-15 02:07:05
    Laravel 队列系统实现及使用教程由 学院君 创建于2年前, 最后更新于 9个月前版本号 #248088 views10 likes0 collects简介注:Laravel 现在提供了基于 Redis 的,拥有美观的后台和配置系统的 Horizon 队列扩展包,...
  • laravel 指定队列运行

    千次阅读 2016-06-02 16:33:34
    通过下面的指令来监听指定队列 php artisan queue:listen --queue="...要推送任务到不同的队列上,你需要将任务先「分类」,甚至可能要指定每个队列能有多少作业器可以运行任务。这并不会推送任务到你在配置文...
  • 每一个进程拥有一个vruntime,每次需要调度的时候就选运行队列中拥有最小vruntime的那个进程来运行,vruntime在时钟中断里面被维护,每次时钟中断都要更新当前进程的vruntime,即vruntime以如下公式逐渐增长: ...
  • 从原理图中可以看到只有当队列满了之后,才会去创建新的线程执行新加入的任务,那么到底有没有可能出现队列未满, 但是运行中的线程个数大于核心线程数? 理论上应该是不可能大于核心线程数,那么有没有意外呢?...
  • 工作队列(work queue)是Linux内核中将操作延期执行的一种机制。因为它们是通过守护进程在用户上下文执行,函数可以睡眠的时间,与内核是无关的。在内核版本2.5开发期间,设计了工作队列,用以替换此前的keventd机制...
  • Oracle高级队列介绍

    万次阅读 2015-02-04 22:34:04
    oracle高级队列介绍 高级队列Advanced Queuing(AQ)在oracle多个版本都可得到。他是oracle原生消息软件并且在每一个版本都在加强。 这篇文章提供了一个AQ的高级概览。尤其是我们将看到如何启动一个队列并进行入列--...
  • 讲的很详细 public final void removeCallbacks (Runnable r)移除那些在消息队列等待的对象. public final void removeCallbacks (Runnable r, Object token) public final void removeC
  • yarn spark 动态调整队列

    千次阅读 2019-03-07 10:44:13
    1.3 MapReduce版本: hadoop jar app.jar -D mapreduce.job.queuename=root.etl....如果是已经在运行中的任务,可以动态调整任务所属队列及其优先级。 2.1 调整优先级 hadoop1.0及以下版本:hadoop job -set...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 724,173
精华内容 289,669
关键字:

运行队列