Kotlin:实现作业调度器的最佳方式(不适用于Android)

new9mtju  于 2023-04-07  发布在  Kotlin
关注(0)|答案(1)|浏览(182)

我的目标:

让一个类有两个方法start和stop,start方法有3个参数:

  • startHour,指示一天中重新开始执行算法的小时
  • isImmediatly,指示第一次且仅第一次是否立即启动算法或等待下一个startHour(如果当前时间较短,则可以从同一天开始,或者如果当前时间较长,则可以从第二天开始)
  • numOfJobs表示并行启动多少个作业。在start中,我必须:
  • 计算开始启动作业之前的等待时间。
  • 计算作业在停止和重新启动之前必须完成其工作的最大时间,这个时间是启动它们和下一个startHour之间的时间差。
  • 启动我动态创建的作业的循环
  • 每个作业执行相同的操作,即:

1.我给仓库打了个电话
1.我运行一个内部循环,每5秒调用一次存储库,直到时间耗尽,或者我得到的结果取决于对存储库的调用,而不管每个作业是完成了工作还是耗尽了时间,在下一个开始时间,我必须重新启动它们。start方法可以被stop方法停止,也可以通过再次调用start()重新启动

第一次实现:

import kotlinx.coroutines.*
import java.time.Duration
import java.time.LocalDateTime
import java.time.LocalTime

class JobScheduler(private val repository: Repository) {

    private val supervisorJob = SupervisorJob()
    private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable ->
        println("Caught exception: $throwable")
    }
private var isRunning = false
    private val jobList = mutableListOf<Job>()

    suspend fun start(startHour: Int, isImmediately: Boolean, numOfJobs: Int) {
        isRunning = true
        var waitTime = if (isImmediately) Duration.ZERO else startHour.amountTimeFromNow()
        while (isRunning) {
            try {
                // Wait the necessary time before launching the jobs
                delay(waitTime.toMillis())

                // Clears all previous jobs that were still running or were scheduled but not yet executed
                jobList.forEach { it.cancel() }
                jobList.clear()

                // Calculate the maximum duration of jobs and create jobs to launch
                val maxJobDuration = startHour.amountTimeFromNow().toMillis()
                supervisorScope {
                    val newJobs = (1..numOfJobs).map {
                        launch(supervisorJob + coroutineExceptionHandler) {
                            withTimeout(maxJobDuration) {
                                while (true) {
                                    // Make the call to the repository
                                    val success: Boolean = repository.call()

                                    // Check if the result is what you want
                                    if (success) {
                                        // The result has been achieved, get out of the loop
                                        break
                                    }

                                    // Wait 5 seconds before making the next call
                                    delay(Duration.ofSeconds(5).toMillis())
                                }
                            }
                        }
                    }

                    // Add new jobs to the list
                    jobList.addAll(newJobs)
                }

                // Wait for the next start time
                waitTime = startHour.amountTimeFromNow()

            } catch (e: CancellationException) {
                // Gracefully handle cancellation
                println("JobScheduler has been cancelled")
                break
            } catch (e: Exception) {
                // Handle any other exceptions
                println("Caught exception: $e")
            }
        }
    }

    fun stop() {
        isRunning = false
        supervisorJob.cancelChildren()
        jobList.clear()
    }

    fun Int.amountTimeFromNow(): Duration {
        val now = LocalDateTime.now()
        val startHour = LocalDateTime.of(now.toLocalDate(), LocalTime.of(this, 0))
        val nextStart = if(now >= startHour){
            startHour.plusDays(1)
        }else{
            startHour
        }
        return Duration.between(now, nextStart)
    }
}
  • 是对的吗 *
e3bfsja2

e3bfsja21#

我不想重写你的代码,但我有一些提示给你。
使用withTimoutmeasureTimeMillis可以使代码更简洁,可读性更好。
不要使用全局作用域,而是使用MainScope()CoroutineScope()。但为了结构并发性,我建议尽可能多地使用coroutineScope{}supervisorScope{}
使用coroutinExceptionHandler处理作业中的异常,如果不希望父作业被取消,请确保使用supervisorJob

相关问题