任务_任务调度 - CSDN
精华内容
参与话题
  • 在线任务平台

    2018-01-17 10:58:29
    基于微信的任务发布接收平台,包含微信、支付宝支付;PHP版
  • 1.任务状态 FreeRTOS可以创建多个任务,但是对于单核cpu来说,在任意给定时间,实际上只有一个任务被执行,这样就可以把任务分成2个状态,即运行状态和非运行状态。 当任务处于运行状态时,处理器就执行该任务的...

    1.任务状态

    FreeRTOS可以创建多个任务,但是对于单核cpu来说,在任意给定时间,实际上只有一个任务被执行,这样就可以把任务分成2个状态,即运行状态和非运行状态。

    当任务处于运行状态时,处理器就执行该任务的代码。处于非运行态的任务,它的所有寄存器状态都保存在自己的任务堆栈中,当调度器将其恢复到运行态时,会从上一次离开运行态时正准备执行的那条指令开始执行。

    如下图所示,从整体上操作系统调度可以看作是把任务从运行态和非运行态来回切换。
    这里写图片描述

    每个任务都有一个任务控制块(TCB),而pxCurrentTCB则保存了当前运行的TCB,当任务发生切换后,pxCurrentTCB选择就绪任务列表里优先级最高的任务轮流执行。

    上面提到的只是一个最粗略的任务状态模型,事实上为了完成复杂的任务调度机制,将任务的非运行态又划分成了3个状态,分别是阻塞状态,挂起状态和就绪状态,完整的状态转移图如下所示:
    这里写图片描述

    从图中可以看到,运行态的任务可以直接切换成挂起、就绪或阻塞状态,但是只有在就绪态里的任务才能直接切换成运行态。

    2.任务列表

    要理解调度器是如何将任务从这些状态切换,首先必须明白任务列表这个概念。处于运行态的任务只有一个,而其他所有任务都处于非运行态,所以一个状态往往存在很多任务,为了让调度器容易调度,把相同状态下的任务通过列表组织在一起。

    • 就绪态

      任务在就绪状态下,每个优先级都有一个对应的列表,最多有configMAX_PRIORITIES个

      static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

    • 暂停态

      当任务被挂起时,任务将会放在暂停列表里:

      static List_t xSuspendedTaskList;

    • 阻塞态

     当任务因为延时或等待事件的发生时会处于阻塞状态,延时任务列表里的任务通过延时的先后顺序由小到大排列,延时列表必须有2个,当延时唤醒后的时间戳会溢出的任务放在溢出任务列表里。等到系统时间戳溢出后,再把溢出任务列表和当前任务列表切换。
    `
    static List_t xDelayedTaskList1;                        /*< Delayed tasks. */
    static List_t xDelayedTaskList2;                        /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */
    static List_t * volatile pxDelayedTaskList;             /*< Points to the delayed task list currently being used. */
    static List_t * volatile pxOverflowDelayedTaskList;     /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */
        if( xConstTickCount == ( TickType_t ) 0U )
        {
            taskSWITCH_DELAYED_LISTS();
        }
    
    #define taskSWITCH_DELAYED_LISTS()                                                                  \
    {                                                                                                   \
        List_t *pxTemp;                                                                                 \
                                                                                                        \
        /* The delayed tasks list should be empty when the lists are switched. */                       \
        configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );                                     \
                                                                                                        \
        pxTemp = pxDelayedTaskList;                                                                     \
        pxDelayedTaskList = pxOverflowDelayedTaskList;                                                  \
        pxOverflowDelayedTaskList = pxTemp;                                                             \
        xNumOfOverflows++;                                                                              \
        prvResetNextTaskUnblockTime();                                                                  \
    }
    

    在阻塞态下除了延时任务列表,还要等待事件发生的任务列表,这在信号量,消息队列,任务通知时都会用到。队列里的任务列表通常有2个,一个是发送,一个是接收,列表项是按照优先级排序的,这里先不展开,以后学习队列的时候再详细分析。

    pxQueue->xTasksWaitingToSend
    pxQueue->xTasksWaitingToReceive
    • pending态
      当任务从挂起或阻塞状态被激活时,如果调度器也处于挂起状态,任务会先放进xPendingReadyList队列,等到调度器恢复时(xTaskResumeAll)再将这些xPendingReadyList里的任务一起放进就绪列表。

      那么为什么要来一个中间步骤而不直接放进就绪任务列表呢?
      这是因为任务从挂起到恢复可能出现优先级大于当前运行任务,高优先级任务要抢占低优先级任务,由于之前调度器被挂起,所以无法执行抢占操作。等调度器恢复后,再将xPendingReadyList里的任务一一取出来,判定是否有抢占操作发生或任务延时到期。

    while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
    {
        pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
        ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
        ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
        prvAddTaskToReadyList( pxTCB );
    
        /* If the moved task has a priority higher than the current
        task then a yield must be performed. */
        //弹出的任务高于当前优先级,需要进行任务抢占
        if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
        {
            xYieldPending = pdTRUE;
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    
    UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
    
    if( uxPendedCounts > ( UBaseType_t ) 0U )
    {
        do
        {
            //检查恢复的任务有没有延时到期的
            if( xTaskIncrementTick() != pdFALSE )
            {
                xYieldPending = pdTRUE;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            --uxPendedCounts;
        } while( uxPendedCounts > ( UBaseType_t ) 0U );
    
        uxPendedTicks = 0;
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    3.列表结构

    列表由列表头和列表项组成,列表头和列表项组成一个双向循环链表。
    列表的结构定义如下

    typedef struct xLIST
    {
        listFIRST_LIST_INTEGRITY_CHECK_VALUE                /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
        configLIST_VOLATILE UBaseType_t uxNumberOfItems;
        ListItem_t * configLIST_VOLATILE pxIndex;           /*< Used to walk through the list.  Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
        MiniListItem_t xListEnd;                            /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
        listSECOND_LIST_INTEGRITY_CHECK_VALUE               /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    } List_t;

    listFIRST_LIST_INTEGRITY_CHECK_VALUE不用管,在代码里宏定义为空。pxIndex表示当前列表的的索引,通过这个值来遍历整个列表,在相同优先级调度时会通过时间片来轮流执行每个任务,这时候就会用到这个值。xListEnd标记列表的结尾是一个简化的列表项,不存储数据,只存储链表的前后指针。
    struct xMINI_LIST_ITEM
    {
    listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. /
    configLIST_VOLATILE TickType_t xItemValue;
    struct xLIST_ITEM * configLIST_VOLATILE pxNext;
    struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
    };
    这里xItemValue固定为0xffffffff,标记链表结束,重新开始新一轮循环。

    真正的列表项则还需要存储该列表项所在的列表和列表项对应的TCB,如果在延时列表里,则xItemValue表示下一次唤醒的时间戳,如果在队列里,则xItemValue表示任务优先级,在一些事件列表里该值还用来表示相关事件。

    struct xLIST_ITEM
    {
        listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE           /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
        configLIST_VOLATILE TickType_t xItemValue;          /*< The value being listed.  In most cases this is used to sort the list in descending order. */
        struct xLIST_ITEM * configLIST_VOLATILE pxNext;     /*< Pointer to the next ListItem_t in the list. */
        struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
        void * pvOwner;                                     /*< Pointer to the object (normally a TCB) that contains the list item.  There is therefore a two way link between the object containing the list item and the list item itself. */
        void * configLIST_VOLATILE pvContainer;             /*< Pointer to the list in which this list item is placed (if any). */
        listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE          /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
    };
    typedef struct xLIST_ITEM ListItem_t;                   /* For some reason lint wants this as two separate definitions. */

    下面还是通过结构图来描述列表、列表项和任务TCB的对应关系:
    这里写图片描述

    每个任务TCB都有2个列表项指针,用来标记任务的状态,分别是状态列表项和事件列表项

    pxTCB->xStateListItem
    pxTCB->xEventListItem

    pxTCB->xStateListItem用来标记任务的状态,pxTCB->xEventListItem表示任务在某一个事件等待队列里。

    4.任务调度

    在移植时,我们把系统时钟中断xPortSysTickHandler加入到了中断向量表,这个中断周期设置为1ms。这个中断是系统的核心,我们称作调度器,在这里会调用xTaskIncrementTick()把时间计数值加1,并检查有哪些延时任务到期了,将其从延时任务列表里移除并加入到就绪列表里。如果到期的任务优先级>=当前任务则开始一次任务切换。如果当前任务就绪态里有多个任务,也需要切换任务,优先级相同需要在一个系统时钟周期的时间片里轮流执行每个任务。另外在应用程序里也可以通过设置xYieldPending的值来通知调度器进行任务切换。

    在一个延时阻塞的任务里,如下面的程序:

    void vTask1(void *pvParameters)
    {
      while(1)
      {  
        UARTprintf("task1 %d\n",i++);
        vTaskDelay(1000/portTICK_RATE_MS);
      }
    }

    程序每1s执行一次,执行完后vTaskDelay会将当前任务添加到延时任务列表里,并强行切换任务。过了1s后系统时钟中断检测到任务该任务延时到期,会重新添加到就绪任务列表里。此时如果延时到期的任务优先级最高,将会被唤醒执行。如果优先级不是最高,那么任务将得不到执行,只有当最高优先级的任务进入阻塞状态才会执行。

    了解信号量和消息队列的同学都知道,通常在编程时一个死循环任务在等待消息或信号量,此时这个任务会卡在那里,直到另外的任务或中断发送了信号量后,这个任务才能往下走。举个例子,有如下代码

    void vTask1(void *pvParameters)
    {
      while(1)
      {  
        xSemaphoreGive(xSemaphore);
        UARTprintf("task1 %d\n",i++);
        vTaskDelay(1000/portTICK_RATE_MS);
      }
    }
    
    
    void vTask2(void *pvParameters)
    {
      while(1)
      {
        xSemaphoreTake( xSemaphore, portMAX_DELAY );
        UARTprintf("task2\n");
      }
    }

    这个代码中任务2一直在等待信号量,处于阻塞状态。而任务1每秒执行1次,唤醒后发送信号量激活任务2。这里先粗略介绍一下信号量的调度机制,后面讲队列的时候在详细介绍。xSemaphoreTake没收到信号量时会将任务加入到阻塞队列,并开始一次任务切换。

    BaseType_t xQueueGenericReceive(
    {
        for( ;; )
        {
            taskENTER_CRITICAL();
            {
                const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
    
                /* Is there data in the queue now?  To be running the calling task
                must be the highest priority task wanting to access the queue. */
                if( uxMessagesWaiting > ( UBaseType_t ) 0 )
                {
                    ......
                    //收到消息或信号量函数返回
                    //使得任务往下执行
                }
                else
                {
                    ......
                }
           }
           taskEXIT_CRITICAL();
           ......
           vTaskSuspendAll();
           ......
           //在这里把任务添加到等待队列,并从就绪任务列表里执行
           vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
           prvUnlockQueue( pxQueue );
           //在这里切换任务,如果是TURE,则在xTaskResumeAll()函数里面已经切换好了
           if( xTaskResumeAll() == pdFALSE )
           {
                //发送切换任务的请求
                //任务后面的代码不会被执行
                //任务被激活后从这里开始执行下一条指令
                portYIELD_WITHIN_API();
           }
       }
    }

    发送信号量时在xQueueGenericSend()函数里会把任务从等待队列中移除,重新加入到就绪列表里,如果优先级比当前任务高,则开始一次任务抢占切换,否则把任务交给调度器切换。

    if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
    {
        /* The unblocked task has a priority higher than
        our own so yield immediately.  Yes it is ok to do
        this from within the critical section - the kernel
        takes care of that. */
        queueYIELD_IF_USING_PREEMPTION();
    }

    5.临界区

    进入临界区即屏蔽中断,但这里不屏蔽所有中断,FreeRTOS有一个临界区中断优先级,在FreeRTOSConfig.h里配置

    #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 

    关闭中断时设置BASEPRI寄存器为该值,这个寄存器会并屏蔽掉掉优先级比设定值低的,而优先级高的不受影响,如果BASEPRI设为0则不屏蔽任何中断。
    假如M3的中断优先级为3位,则191(0b10111111)的前3位有效,即优先级为5,此时优先级为5~7的中断优先级低于设定值值会被屏蔽,而优先级为0~4的中断优先级高于设定值不受影响,不受影响的中断称作非临界区中断,不能调用FreeRTOS的API,否则会破会系统数据。
    进入临界区时会屏蔽临界区内的所有中断,进入和离开临界区的API需要成对使用

    taskENTER_CRITICAL()
    ......
    taskEXIT_CRITICAL() 

    这是一个宏定义,我们看最后的实现:

    void vPortEnterCritical( void )
    {
        portDISABLE_INTERRUPTS();
        uxCriticalNesting++;
    
        /* This is not the interrupt safe version of the enter critical function so
        assert() if it is being called from an interrupt context.  Only API
        functions that end in "FromISR" can be used in an interrupt.  Only assert if
        the critical nesting count is 1 to protect against recursive calls if the
        assert function also uses a critical section. */
        if( uxCriticalNesting == 1 )
        {
            configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
        }
    }
    
    void vPortExitCritical( void )
    {
        configASSERT( uxCriticalNesting );
        uxCriticalNesting--;
        if( uxCriticalNesting == 0 )
        {
            portENABLE_INTERRUPTS();
        }
    }

    这里我们发现多了一个嵌套计数值,这是为了防止调用函数进入临界区出现嵌套时,里面那一层taskEXIT_CRITICAL()提前将中断打开了,外面那一层的数据就不受保护了。

    在中断里屏蔽临界区用一种不同的方式,进入临界区时时先读取当前BASEPRI寄存器的值,退出后在设定为之前读取的值,所以发生嵌套后里面那一层并不改变寄存器的值。

    uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
    ......
    portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

    使用这种临界区屏蔽中断的API函数的结尾都加上了FromISR后缀表示在中断里调用。

    这里有一个问题,既然这2种临界区的实现效果相同,为什么不统一成一种?这个问题一直没想明白,尝试给出一个强行的解释,如果taskENTER_CRITICAL()用在中断里,则会触发configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );断言,所以只用在任务里,但是非临界区的中断发生也会触发这个断言,所以用这个断言解释感觉不是很说的通。那么任务里为什么不用portSET_INTERRUPT_MASK_FROM_ISR取代taskENTER_CRITICAL()呢?这里可能是为了方便的角度考虑,因为portSET_INTERRUPT_MASK_FROM_ISR这种方式还要额外再定义一个全局变量,在临界区较多的任务代码里使用起来比较麻烦。

    展开全文
  • 代码生成阶段的主要任务是:

    千次阅读 2016-07-20 21:47:44
    代码生成阶段的主要任务是: 正确答案: C 你的答案: 空 (错误) 把高级语言翻译成机器语言 把高级语言翻译成汇编语言 把中间代码变换成依赖具体机器的目标代码 把汇编语言翻译成机器语言...

    代码生成阶段的主要任务是:

    正确答案: C   你的答案: 空 (错误)

    把高级语言翻译成机器语言
    把高级语言翻译成汇编语言
    把中间代码变换成依赖具体机器的目标代码
    把汇编语言翻译成机器语言


    源码 ->(扫描)-> 标记 ->(语法分析)-> 语法树 ->(语义分析)-> 标识语义后的语法树 ->(源码优化)-> 中间代码 ->(代码生成)-> 目标机器代码 ->(目标代码优化)-> 最终目标代码


    展开全文
  • 最近在做一个工作流审批的项目,类似与申请一个请假,但是在完成任务的时候需要动态指定下一级人物,在最开始了解的时候,书上都是直接根据taskId来完成,后来终于实现了这个动态指定下一级处理人的功能。...

    最近在做一个工作流审批的项目,类似与申请一个请假,但是在完成任务的时候需要动态指定下一级人物,在最开始了解的时候,书上都是直接根据taskId来完成,后来终于实现了这个动态指定下一级处理人的功能。
    我们的项目是这么一个流程
    工作流
    那么例如我是一个提交申请的人,例如是一个标准的请假,那么我需要完成当前的人物,还需要动态的指定下一级的是谁来审批,可是有一个前提,那就是我是不知道是谁来完成我的审批,我只需要直接提交申请的就可以。那如果我们是对工作流信息进行审批的人呢,今天我们就看一下activiti的TaskListener。

    TaskListener

    TaskListener,他类似与C#中委托的功能,他能准确的再你执行完成当人物的方法时去调用该接口中你需要执行的一个方法notify。而我就是在这个方法里边去指定的他的下一级是谁。下边我以学术审批的功能来将一下我是如何实现这个的。

    代码实现

    添加Listener

    添加
    单机new按钮
    添加2
    选择create,单机Select class,然后选择我们实现了TaskListener的类就可以了。

    代码

    既然是完成,那么我们就直接来一个完成的代码

        @RequestMapping("/completeByTeacher")
        public String throwApply(HttpServletRequest request,
                HttpServletResponse response) {
            // 1.获取页面上要处理的任务ID,按钮信息,评论信息
             String userId = request.getParameter("usercode");
            //String userId = "456";
            String strTaskId = request.getParameter("taskId");
            String isapprove = request.getParameter("msg");
            String comment = request.getParameter("comment");
            // String strTaskId = "11209";
            // 将获取的任务id的字符串转换成数组
            String[] taskIds = strTaskId.split(",");
    
            try {
                // 2.循环完成任務
                for (String taskId : taskIds) {
                    // String taskId="3212";
    
                    // 2.1根据人物ID查询流程实力ID
                    Task task = processEngine.getTaskService().createTaskQuery()
                            .taskId(taskId).singleResult();
                    // 获取流程实例ID
                    String processInstance = task.getProcessInstanceId();
                    // 2.2根据流程实例ID,人物ID,评论的消息,保存教师或者学术对与该学生申请的评论信息
                    processEngine.getTaskService().addComment(taskId,
                            processInstance, comment);
    
                    EngineController engineController = new EngineController();
                    // 2.3查询当前任务后边的连线名称
                    List<String> list = findOutComeListByTaskId(taskId);
                    if (list.size() > 0 && list != null) {
                        Map<String, Object> map = new HashMap<String, Object>();
                        // 如果是批准操作,则根据连线名称和当前的任务名称,完成操作。
                        map.put("outcome", isapprove);
                        // 2.4如果又连线名称,则根据连线和任务ID完成人物
                        engineController.compalete(taskId, map);
                    } else {
                        // 2.4如果只有任务id,连线没有名称,则直接完成当前任务,并修改用户的当前状态。
                        intershipBean.updateUserInfoByID("itoo_intership", userId,
                                "1");
                        processEngine.getTaskService().complete(taskId);
    
                    }
                }
            } catch (Exception e) {
                // 报错
                return "redirect:/queryTaksByRole";
            }
            return "redirect:/queryTaksByRole";
    
        }

    上边是我的完成人物,但是我需要根据前台我选择的是通过或者驳回,来执行,那么我就需要判断我的这个任务之后的连线,很明显我的是一个通过和驳回,那么获取前台按钮的名称就可以了。然后去执行,那么我们的方法如下:

    // 一直任务ID,查询processDefinitionEntity对象,从而获取
        // 当前任务完成之后的连线名称,并设置到List<String>对象中
        public List<String> findOutComeListByTaskId(String taskId) {
            // 返回连线的名称集合
            List<String> list = new ArrayList<String>();
    
            // 1.使用任务ID查询任务对象
            Task task = processEngine.getTaskService().createTaskQuery()
                    .taskId(taskId).singleResult();
            // 2.获取流程定义ID
            String processDefinitionId = task.getProcessDefinitionId();
            // 3.查询流程定义(ProcessDefinitionEntity)的实体对象
            ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) processEngine
                    .getRepositoryService().getProcessDefinition(
                            processDefinitionId);
            // 使用任务对象Task获取流程实例ID
            String processInstanceId = task.getProcessInstanceId();
            // 使用历程实例ID,查询正在执行的对象表,返回流程实例对象
            ProcessInstance pi = processEngine.getRuntimeService()
                    .createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
    
            // 4.获取当前的活动
            ActivityImpl activityImpl = processDefinitionEntity.findActivity(pi
                    .getActivityId());
    
            // 5.获取当前活动完成之后连线的名称
            List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
            if (pvmList != null && pvmList.size() > 0) {
                for (PvmTransition pvm : pvmList) {
                    String name = (String) pvm.getProperty("name");
                    if (name != null) {
                        list.add(name);
                    }
                }
            }
            return list;
        }

    Listener的实现类

    /**用来指定任务的办理人*/
        @Override
        public void notify(DelegateTask delegateTask) {
                //如果任务的个数大于1的话,则ID为RoleID
                    String sql = "select * From tb_relation where intershipid='" + processDefId + "' and childCode='" + lastassignee + "'";
    
                    Dbhelper dbCourseCurrentadd = new Dbhelper();
                     dbCourseCurrentadd.setSql(sql.toString());      
                     List<Map<String,Object>> listmap =  dbCourseCurrentadd.findItemById();
    
                     if(listmap.size() > 0 && listmap!=null){
                         Map<String, Object> m = listmap.get(0);    
                         nextLevel = (String) m.get("fatherCode");
                     }
        }
    

    需要说明的是DelegateTask这个参数中又流程实例的ID,我们可以根据这个ID,找到我们是做的哪一个审批,然后去查询相应的下一级办理人。因为我又一个tb_relation的表来存储下一级,所以直接查询就可以了。

    小结

    当然我们完全可以不用TaskListener的方法来实现,只是这样的话,我们是不是就可以直接达到服用,这个流程可以用这个类,其他的流程也是直接添加一下Listener,然后就不用管这里的逻辑了,能更好的实现解耦。

    展开全文
  • 游戏任务系统设计思路

    千次阅读 2018-02-27 21:20:01
    表结构id range display title content param_name param_value before_id attachments 2 day 1 击杀训练 累计击杀10个敌人 day_kill 10 0 [{"name": "diamond", "number"...

    表结构

    id

    range

    display

    title

    content

    param_name

    param_value

    before_id

    attachments

    2

    day

    1

    击杀训练

    累计击杀10个敌人

    day_kill

    10

    0

    [{"name": "diamond", "number": 1}]

    201

    month

    1

    签到1天

    签到1天

    month_signin

    1

    0

    [{"name": "diamond", "number": 1}]

    202

    month

    1

    签到2天

    签到2天

    month_signin

    2

    201

    [{"name": "diamond", "number": 1}]

    203

    month

    1

    签到3天

    签到3天

    month_signin

    3

    202

    [{"name": "diamond", "number": 1}]

    344

    all

    1

    2级

    账号升至2级

    level

    2

    0

    [{"name": "diamond", "number": 1}]

    345

    all

    1

    3级

    账号升至3级

    level

    3

    344

    [{"name": "diamond", "number": 1}]

    346

    all

    1

    4级

    账号升至4级

    level

    4

    345

    [{"name": "diamond", "number": 1}]

    347

    all

    1

    5级

    账号升至5级

    level

    5

    346

    [{"name": "diamond", "number": 1}]

    ange:任务类型,day为日常任务,month为月任务(签到任务),all为成就任务;
    display:是否显示
    title:任务标题
    content:任务说明
    param_name:任务数值参考变量名
    param_value:任务完成数值
    before_id:前置任务id
    attachments:任务奖励

    说明

    我们取id=345这一条来进行说明:
    param_name= level,param_value=3,代表用户的数值统计(用户的数值统计自己设计)中,变量level(也就是用户等级)需要达到3,这个任务才能完成。如果没有达到3级,我们也可以根据数值显示任务进度条
    before_id=344,代表这个任务的前置任务是id=344的账号升至2级这个任务,也就是需要领取了2级的奖励后,3级的任务才显示出来
    range=all,代表这是个成就任务,如果range=month则代表这是个月任务,比如说签到任务,而月任务或者日任务对应的param_name变量一般会在每月或每日进行重置
    展开全文
  • 任务学习-multi-learning总结

    万次阅读 多人点赞 2018-11-17 10:08:08
    介绍   在机器学习(ML)中,...虽然通常可以通过这种方式使模型达到可接受的性能,但是由于我们的关注点集中在单个任务上,我们忽略了可能帮助优化度量指标的其它信息。具体来说,这些信息来自相关任务的训练信...
  • YARN任务提交流程

    千次阅读 2017-07-03 10:01:47
    yarn取代了以前hadoop中jobtracker(后面简写JT)的角色,因为以前JT的 任务过重,负责任务的调度、跟踪、失败重启等过程,而且只能运行mapreduce作业,不支持其他编程模式,这也限制了JT使用范围,而yarn应运而 生...
  • SpringBoot定时任务的使用 一、使用Spring Task 1.引入依赖 首先在我们pom.xml文件中引入spring-boot-starter依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId...
  • SpringBoot-定时任务-集群篇

    万次阅读 2019-10-26 17:42:34
    前面一篇文章(SpringBoot-定时任务)中介绍了如何用SpringBoot框架中的注解方式来实现定时任务,这种方式的好处是不使用第三方的依赖,仅凭几个方便的注解,即可编写一个简单的定时任务处理。 实际开发中为了满足复杂...
  • Spring+Quartz实现定时任务的配置方法

    万次阅读 多人点赞 2019-07-31 19:16:18
    &lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="... xmlns:context="...
  • java 定时任务之一 @Scheduled注解(第一种方法)

    万次阅读 多人点赞 2017-12-12 22:09:22
    使用spring @Scheduled注解执行定时任务: 步骤: 1.xmlns 添加: http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd xmlns:task=...
  • 现在的web项目中很多场景下都有要执行定时任务的需求,比如说每隔5秒将redis中的统计当天注册用户数持久化到数据库。现在项目中实现这一功能,一般用quartz这一工具框架,但是这个对于一个新手来说比较麻烦,各种查...
  • SpringQuartz定时任务的cron表达式书写

    万次阅读 2018-09-17 15:09:28
    SpringQuartz定时任务的使用,要配置这个定时任务什么时候执行,周期是多少,周期内执行多少次,这个都是cron表达式来控制的,下面详解一下这个cron表达式。 一、先来举些例子 【1】0 0 10,14,16 * * ? 每天上午10...
  • 使用spring @Scheduled注解执行定时任务

    万次阅读 多人点赞 2012-07-14 09:31:34
    以前框架使用quartz框架执行定时调度问题、老大说这配置太麻烦、每个调度都需要多加在spring的配置中、能不能减少配置的量从而提高开发效率、最近看了看spring的 scheduled的使用注解的方式进行调度、感觉很方便、...
  • Spring 整合 Quartz 实现动态定时任务(附demo)

    万次阅读 多人点赞 2016-05-01 18:57:24
    最近项目中需要用到定时任务的功能,虽然Spring 也自带了一个轻量级的定时任务实现,但感觉不够灵活,功能也不够强大。在考虑之后,决定整合更为专业的Quartz来实现定时任务功能。普通定时任务首先,当然是添加依赖...
  • 定时任务框架Quartz-(一)Quartz入门与Demo搭建

    万次阅读 多人点赞 2018-07-10 13:57:07
    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能: 持久性作业 - 就是保持调度...
  • springboot 定时任务开发配置

    万次阅读 2019-07-19 15:24:58
    相关链接:https://blog.csdn.net/xubenxismile/article/details/96453704 一、添加依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>.../art...
  • crontab定时任务不执行的原因

    万次阅读 多人点赞 2017-01-16 18:35:20
    我在网上找的时候发现网上主要...如果停止了就无法执行任何定时任务了,解决的方法是打开它: crond 或 service crond start 如果提示crond命令不存在,可能被误删除了,CentOS下可以通过这个命令重新安装: yum...
  • SpringBoot几种定时任务的实现方式

    万次阅读 多人点赞 2018-02-01 09:39:16
    原文地址:SpringBoot几种定时任务的实现方式 定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行...
  • 基于Springboot执行多个定时任务并且动态获取定时任务信息 基于Springboot执行多个定时任务并且动态获取定时任务信息 简介 说明 SpringApplication-启动类 动态获取定时任务信息 mapper service service impl ...
1 2 3 4 5 ... 20
收藏数 2,698,438
精华内容 1,079,375
关键字:

任务