当前位置:

首页 > SpringBoot Schedule调度任务的动态管理方法是什么

SpringBoot Schedule调度任务的动态管理方法是什么

前言定时任务动态管理分为两种方式:方式一:Web前台配置Trigger触发器(关联Cron)、ThreadPoolTaskScheduler类创建Scheduler方式下进行Schedule调度任务的动态管理方式二:基于已创建的Schedule调度任务的动态管理,即以组件类@Scheduled注解声明Schedule调度,在启动程序前一次性初始化,如:@ComponentpublicclassTestTask{privateDateTimeFormatterdf=DateTimeFormatter.ofP

前言

定时任务动态管理分为两种方式:

方式一:Web前台配置Trigger触发器(关联Cron)、ThreadPoolTaskScheduler类创建Scheduler方式下进行Schedule调度任务的动态管理

方式二:基于已创建的Schedule调度任务的动态管理,即以组件类 @Scheduled注解声明Schedule调度,在启动程序前一次性初始化,如:

@Component
public class TestTask {
    private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @Scheduled(cron = "0/2 * * * * ?")
    public void robReceiveExpireTask() {
        System.out.println(df.format(LocalDateTime.now()) + "测试测试");
    }
}

缺陷:目前无法在运行期间增加Schedule以及stop、Start、Reset等管理。

一、架构流程图

SpringBoot Schedule调度任务的动态管理方法是什么

二、代码实现流程

架构为SpringBoot + Spring + mybatis-plus

1.引入库

pom.xml



    
        merak-hyper-automation-boot
        com.merak.automation
        1.0.0
    
    4.0.0
    automation-quartz
    jar
    
        
            aliyun
            aliyun Repository
            http://maven.aliyun.com/nexus/content/groups/public
            
                false
            
        
    
    
        
        
            org.springframework
            spring-context-support
        
        
        
            org.springframework
            spring-web
        
        
        
            mysql
            mysql-connector-java
        
        
        
            com.alibaba
            druid-spring-boot-starter
        
        
            org.apache.commons
            commons-lang3
        
        
            jakarta.validation
            jakarta.validation-api
        
        
            com.alibaba
            fastjson
        
        
            commons-io
            commons-io
        
        
            com.fasterxml.jackson.core
            jackson-annotations
        
        
        
            org.springframework.boot
            spring-boot-starter-quartz
            2.2.5.RELEASE
        
    
    
        
            
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

resources目录下文件/application.yml:

spring:

profiles:

active: dev

resources目录下文件/application-dev.yml:

server:
  port: 12105
  servlet:
    context-path: /automation-quartz

management:
  endpoints:
    web:
      exposure:
        include: '*'

# Spring配置
spring:
  resources:
    static-locations: classpath:/static/,classpath:/templates/
  mvc:
    throw-exception-if-no-handler-found: true
    static-path-pattern: /**
  application:
    name: automation-workflow
  main:
    allow-bean-definition-overriding: true
  # 文件上传
  servlet:
    multipart:
      # 单个文件大小
      max-file-size: 2000MB
      # 设置总上传的文件大小
      max-request-size: 4000MB
  #json 时间戳统一转换
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  aop:
    proxy-target-class: true
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    dynamic:
      druid:
        # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
        # 连接池的配置信息
        # 初始化大小,最小,最大
        initial-size: 1
        min-idle: 1
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,wall,slf4j
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      datasource:
        master:
          url: jdbc:mysql://127.0.0.1:3308/merak_dev?characterEncoding=UTF-8&useUnicode=true&useSSL=false
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver

#mybatis plus 设置
mybatis-plus:
  mapper-locations: classpath*:com/merak/hyper/automation/persist/**/xml/*Mapper.xml
  global-config:
    # 关闭MP3.0自带的banner
    banner: false
    db-config:
      id-type: ID_WORKER_STR
      # 默认数据库表下划线命名
      table-underline: true
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    com.merar.hyper: debug
    com.merak.hyper.automation.persist.**.mapper: debug
    org.springframework: warn

2.代码流程

启动MerakQuartzApplication类

package com.merak.hyper.automation;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * @author chenjun
 * @version 1.0
 * @ClassName: MerakQuartzApplication
 * @description: 工单任务调度
 * @date 2022/9/22 10:30
 */
@EnableScheduling
@EnableAsync
@MapperScan(basePackages = {"com.merak.hyper.automation.persist.**.mapper"})
@SpringBootApplication(scanBasePackages = {"com.merak.hyper.automation.**"}, exclude = {SecurityAutoConfiguration.class})
public class MerakQuartzApplication {
    public static final Logger log = LoggerFactory.getLogger(MerakQuartzApplication.class);
    public static void main(String[] args) {
        SpringApplication.run(MerakQuartzApplication.class, args);
    }
    private int taskSchedulerCorePoolSize = 15;
    private int awaitTerminationSeconds = 60;
    private String threadNamePrefix = "taskExecutor-";
    /**
     * @description: 实例化ThreadPoolTaskScheduler对象,用于创建ScheduledFuture scheduledFuture
     */
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(taskSchedulerCorePoolSize);
        taskScheduler.setThreadNamePrefix(threadNamePrefix);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(false);
        taskScheduler.setAwaitTerminationSeconds(awaitTerminationSeconds);
        /**需要实例化线程*/
        taskScheduler.initialize();
//        isinitialized = true;
        log.info("初始化ThreadPoolTaskScheduler ThreadNamePrefix=" + threadNamePrefix + ",PoolSize=" + taskSchedulerCorePoolSize
                + ",awaitTerminationSeconds=" + awaitTerminationSeconds);
        return taskScheduler;
    }
    /**
     * @description: 实例化ThreadPoolTaskExecutor对象,管理asyncTask启动的线程,应用类为 ScheduledHelper 
     */
    @Bean("asyncTaskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setThreadNamePrefix("asyncTaskExecutor-");
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(60);
        //修改拒绝策略为使用当前线程执行
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //初始化线程池
        taskExecutor.initialize();
        return taskExecutor;
    }
}

一、启动时项目启动时,加载任务关联的触发器,并全量执行流程。

initLineRunner类:

package com.merak.hyper.automation.Scheduling;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.merak.hyper.automation.persist.entity.AutoTriggerInfo;
import com.merak.hyper.automation.persist.entity.BusWorkflow;
import com.merak.hyper.automation.persist.service.IAutoTriggerInfoService;
import com.merak.hyper.automation.persist.service.IBusWorkflowService;
import com.merak.hyper.automation.util.CommonUtil;
import com.merak.hyper.automation.util.ScheduleUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
 * 项目启动时,加载数字员工关联的触发器,并全量执行
 * @Date: 2020/12/25:16:00
 **/
@Component
@Order(1)
public class initLineRunner implements CommandLineRunner {
    public static final Logger log = LoggerFactory.getLogger(initLineRunner.class);
    private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @Autowired
    private TaskService taskService;
    @Autowired
    private IAutoTriggerInfoService triggerInfoService;
    @Autowired
    private IBusWorkflowService workflowService;
    @Override
    public void run(String... args) {
        log.info("项目启动:加载数字员工关联的触发器信息并全量执行," + df.format(LocalDateTime.now()));
        QueryWrapper wrapper = new QueryWrapper<>();
        wrapper.eq("wf_type", "3");//3:云托管
        wrapper.eq("wf_state", "1");
        List busWorkflows = workflowService.list(wrapper);
        List triggerInfos =  triggerInfoService.list();
        if( 0 == busWorkflows.size() || 0 == triggerInfos.size() ){
            log.info("数字员工关联的触发器信息不正确,员工记录数:"+busWorkflows.size()+",触发器记录数:"+triggerInfos.size());
        }
        else{
            //数字员工关联的触发器信息
            Map loadWfidAndTriggerInfo = CommonUtil.loadWfidAndTriggerInfo(busWorkflows,triggerInfos);
            Iterator> entries = loadWfidAndTriggerInfo.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = entries.next();
                String wfId = entry.getKey();
                BusWorkflow workflow = busWorkflows.stream().filter( t -> wfId.equals(t.getWfId()) ).findAny().orElse(null);
                if( null != workflow ){
                    ScheduleUtil.start(new ScheduleTask(wfId,String.valueOf(workflow.getWfCreateuserId()),taskService), entry.getValue());
                }
            }
            log.info("数字员工关联的触发器信息全量执行完成,数字员工定时个数:"+loadWfidAndTriggerInfo.size()+","+df.format(LocalDateTime.now()));
        }
    }
}
核心代码:
```java
  ScheduleUtil.start(new ScheduleTask(wfId,String.valueOf(workflow.getWfCreateuserId()),taskService), entry.getValue());

Scheduler管理工具类:启动、取消、修改等管理

package com.merak.hyper.automation.util;
import com.merak.hyper.automation.Scheduling.ScheduleTask;
import com.merak.hyper.automation.persist.entity.AutoTriggerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
/**
 * @version 1.0
 * @ClassName: ScheduleUtil
 * @description: Scheduler管理工具类:启动、取消、修改等管理
 */
public class ScheduleUtil {
    public static final Logger log = LoggerFactory.getLogger(ScheduleUtil.class);
    private static ThreadPoolTaskScheduler threadPoolTaskScheduler = SpringContextUtils.getBean(ThreadPoolTaskScheduler.class);
    //存储[数字员工wfI,dScheduledFuture]集合
    private static Map> scheduledFutureMap = new HashMap<>();
    /**
     * 启动
     *
     * @param scheduleTask 定时任务
     * @param triggerInfo
     */
    public static boolean start(ScheduleTask scheduleTask, AutoTriggerInfo triggerInfo) {
        String wfId = scheduleTask.getId();
        log.info("启动数字员工"+wfId+"定时任务线程" + scheduleTask.getId());
        ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduleTask, new CronTrigger(triggerInfo.getLogicConfig()));
        scheduledFutureMap.put(wfId, scheduledFuture);
        return true;
    }
    /**
     * 取消
     *
     * @param scheduleTask 定时任务
     */
    public static boolean cancel(ScheduleTask scheduleTask) {
        log.info("关闭定时任务线程 taskId " + scheduleTask.getId());
        ScheduledFuture scheduledFuture = scheduledFutureMap.get(scheduleTask.getId());
        if (scheduledFuture != null && !scheduledFuture.isCancelled()) {
            scheduledFuture.cancel(false);
        }
        scheduledFutureMap.remove(scheduleTask.getId());
        return true;
    }
    /**
     * 修改
     *
     * @param scheduleTask 定时任务
     * @param triggerInfo
     */
    public static boolean reset(ScheduleTask scheduleTask, AutoTriggerInfo triggerInfo) {
        //先取消定时任务
        cancel(scheduleTask);
        //然后启动新的定时任务
        start(scheduleTask, triggerInfo);
        return true;
    }
}

ScheduleTask类:ScheduleTask任务类

package com.merak.hyper.automation.Scheduling;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @version 1.0
 * @ClassName: ScheduleTask
 * @description: ScheduleTask,关联任务id、用户id和具体执行的TaskService类,实现Runnable类
 */
public class ScheduleTask implements Runnable {
    private static final int TIMEOUT = 30000;
    private String id;
    private String userId;
    private TaskService service;
    public static final Logger log = LoggerFactory.getLogger(ScheduleTask.class);
    public String getId() {
        return id;
    }
    /**
     * @param id      任务ID
     * @param service 业务类
     */
    public ScheduleTask(String id, String userId, TaskService service) {
        this.id = id;
        this.userId = userId;
        this.service = service;
    }
    @Override
    public void run() {
        log.info("ScheduleTask-执行数字员工消息的发送,id:"+ this.id + ",用户id:"+userId);
        service.work(this.id,this.userId);
    }
}
/**
 * @version 1.0
 * @ClassName: TaskService
 * @description: TaskService
 */
public interface TaskService {
    /**
     * 业务处理方法
     * @param keyword 关键参数
     * @param userId
     */
    void work(String keyword,String userId);
}
/**
 * @description: TaskService实现类,具体执行定时调度的业务
 */
@Service
public class TaskServiceImpl implements TaskService {
    public static final Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);
    @Autowired
    private IAutoDeviceInfoService deviceInfoService;
    @Override
    public void work(String wfId,String userId) {
        try {
            log.info("定时任务:根据数字员工wfId"+ wfId +",用户id:"+userId+",发送消息...");
            //sendRobotMsg(wfId,userId);
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

二、通过WEB配置的变更,动态管理定时任务

ScheduledController类:scheduled Web业务层:启动、取消、修改等管理schedule

调度任务信息变更(如1:Trigger Cron变更 2:任务停止 3:任务新增加等)

package com.merak.hyper.automation.controller;
import com.merak.hyper.automation.common.core.domain.AjaxResult;
import com.merak.hyper.automation.common.core.vo.ScheduledApiVo;
import com.merak.hyper.automation.persist.entity.AutoTriggerInfo;
import com.merak.hyper.automation.persist.service.IAutoTriggerInfoService;
import com.merak.hyper.automation.util.ScheduledHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * @version 1.0
 * @ClassName: ScheduledController
 * @description: scheduled Web业务层:启动、取消、修改等管理schedule
 */
@RestController
@RequestMapping("/api/scheduled")
public class ScheduledController {
    public static final Logger log = LoggerFactory.getLogger(ScheduledController.class);
    @Autowired
    private IAutoTriggerInfoService triggerInfoService;
    @Autowired
    private ScheduledHelper scheduledHelper;
    @PostMapping("/add")
    public AjaxResult addScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){
        AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId());
        scheduledHelper.addScheduleds(scheduledApiVo,autoTriggerInfo);
        return AjaxResult.success();
    }
    @PostMapping("/reset")
    public AjaxResult resetScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){
        AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId());
        scheduledHelper.resetScheduleds(scheduledApiVo,autoTriggerInfo);
        return AjaxResult.success();
    }
    @PostMapping("/stop")
    public AjaxResult stopScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){
        AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId());
        scheduledHelper.stopScheduleds(scheduledApiVo);
        return AjaxResult.success();
    }
}
ScheduledHelper类:对外提供ScheduledHelper管理:创建、变更、停止
```java
package com.merak.hyper.automation.util;
import com.merak.hyper.automation.Scheduling.ScheduleTask;
import com.merak.hyper.automation.Scheduling.TaskService;
import com.merak.hyper.automation.common.core.vo.ScheduledApiVo;
import com.merak.hyper.automation.persist.entity.AutoTriggerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
 * @version 1.0
 * @ClassName: ScheduledHelper
 * @description:对外提供ScheduledHelper管理:创建、变更、停止
 */
@Component
public class ScheduledHelper {
    public static final Logger log = LoggerFactory.getLogger(ScheduledHelper.class);
    /**
     * @description: 对外(Web)提供异步的Scheduleds增加操作
     */
    @Async("asyncTaskExecutor")
    public void addScheduleds(ScheduledApiVo scheduledApiVo, AutoTriggerInfo triggerInfo) {
        //addSchedule任务
        log.warn("创建原数字员工["+scheduledApiVo.getWfId()+"],同步启动Schedule任务");
        TaskService taskService = SpringContextUtils.getBean(TaskService.class);
        ScheduleUtil.start(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService), triggerInfo);
    }
    @Async("asyncTaskExecutor")
    public void resetScheduleds(ScheduledApiVo scheduledApiVo,AutoTriggerInfo triggerInfo) {
        //cron值改变,变更Schedule任务
        log.warn("数字员工["+scheduledApiVo.getWfId()+"]关联的触发器信息cron值改变,变更Schedule任务");
        TaskService taskService = SpringContextUtils.getBean(TaskService.class);
        ScheduleUtil.reset(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService), triggerInfo);
    }
    @Async("asyncTaskExecutor")
    public void stopScheduleds(ScheduledApiVo scheduledApiVo) {
        //移除Wfid,停止原Schedule任务
        log.warn("原数字员工["+scheduledApiVo.getWfId()+"]无效,同步停止Schedule任务");
        TaskService taskService = SpringContextUtils.getBean(TaskService.class);
        ScheduleUtil.cancel(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService));
    }
}

SpringContextUtils类:

package com.merak.hyper.automation.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * @version 1.0
 * @ClassName: SpringContextUtils
 * @description: 加载Class对象
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }
    public static  T getBean(Class requiredType) {
        return applicationContext.getBean(requiredType);
    }
    public static  T getBean(String name, Class requiredType) {
        return applicationContext.getBean(name, requiredType);
    }
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }
    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }
    public static Class getType(String name) {
        return applicationContext.getType(name);
    }
}

ScheduledApiVo类:

import java.io.Serializable;
/**
 * @version 1.0
 * @ClassName: ScheduledApiVo
 * @description: scheduled Web业务层Api传递参数Vo类
 */
public class ScheduledApiVo implements Serializable {
    private String wfId;
    private String userId;
    private String triggerId;
    //set get 略
}

最终:Web端通过发送Http请求 ,调用ScheduledHelper管理类接口,实现Scheduled创建、变更、停止操作

  log.info("3:云托管更新启动数字员工操作");
  ScheduledApiVo scheduledApiVo = new ScheduledApiVo();
  scheduledApiVo.setWfId(wfId);
  scheduledApiVo.setUserId(String.valueOf(updateUserId));
  scheduledApiVo.setTriggerId(newTriggerInfo.getId());
  String webHookBody = JSON.toJSONString(scheduledApiVo);
  EmsApiUtil.SendQuartzMessage(url, "add", webHookBody);
 ******************** 分隔     ************************
  public static boolean SendQuartzMessage(String quartzUrl, String method, String webHookBody){
      boolean result = false;
      try{
          //org.apache.httpcomponents.httpclient sendPost,pom依赖如下dependency
          String resp = HttpClientUtil.sendPostByJson(quartzUrl+"/"+method, webHookBody,0);
          if( "error".equals(resp) || resp.contains("405 Not Allowed")){
              log.error("调用任务调度中心消息发送失败,地址:"+quartzUrl);
          }
          else {
              JSONObject jsonObject = JSON.parseObject(resp);
              if( "200".equals(String.valueOf(jsonObject.get("code"))) ){
                  result = true;
              }
              else{
                  log.error("调用任务调度中心失败,msg:"+String.valueOf(jsonObject.get("msg")));
              }
          }
      }catch (Exception e){
          log.error("调用任务调度中心失败,msg:"+e.getMessage());
      }
      return result;
  }
    
        org.apache.httpcomponents
        httpclient
        4.5.2
    
本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
相关文章 更多
using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

一个 memwatch 实战案例:定位野指针问题
一个 memwatch 实战案例:定位野指针问题

内存监控工具的价值与挑战在软件开发,尤其是使用C/C++这类手动管理内存的语言时,内存错误是程序员最常遭遇的难题之一。其中,野指针问题因其隐蔽性和破坏性,往往成为最难定位的“幽灵”缺陷。它可能潜伏在代码中,在特定条件下才被触发,导致程序崩溃、数据损坏或难以预测的行为。传统的调试手段,如打印日志或使用

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。