Acanis Posted February 22, 2024 Posted February 22, 2024 Hey, what is the best way to do a scheduled task queue? Iam usally using OnEvent in my GUI and if I have to time tasks, I use AdlibRegister and AdlibUnRegister. Thats working pretty well: one task doesnt disturb the other ones. But I cant see the status of the Adlibs and Im always happy to learn about better ways to do something I found the "_AdlibEnhance.au3", which should improve the possibilites, but its still Adlib. A little bit more specific on the use case: Quote A GUI with different parts that are to be updated from different data sources at certain intervals. There is also the option for the user to insert additional tasks (send an immediate update or reminder at a certain time or output an analysis from the available data, etc.) and to delete tasks. A task would be calling a function (in best case I can use parameters). However, tasks should not interrupt each other and AutoIt does not want to do multithreading or anything like that. And for debugging purposes, I would also like to be able to see which tasks are currently scheduled, in which order and how long it will take or approximately when it will happen. I don't believe that this is a special use case and think that there must be a UDF for this that I just haven't found yet or that there are at least better solutions than "Adlib". Maybe some "sorted list" (by timestamps) and checking the top entry timestamp every few ms in the main loop? And If I add a new entry, I sort the list again? And it just checks, if the tasks timestamp is in the past?! And to add entries, for example "check the weather every 5 min", I just calculate the target time stamp and I use the "check weather function" to add another entry, after the check is done. Not sure, how I call the functions from something like this. Maybe with a string of the function in the list and then using "Eval()"?!
Andreik Posted February 23, 2024 Posted February 23, 2024 Not really sure what do you want to achieve but you can use a dummy control to process responses from your functions outside the functions body, even using blocking functions. HotKeySet('{ESC}', 'Quit') AdlibRegister('F1', 15000) AdlibRegister('F2', 5000) $hGUI = GUICreate('', 0, 0, 0, 0, 0x80000000) $hDummy = GUICtrlCreateDummy() GUISetState(@SW_SHOW) While True Switch GUIGetMsg() Case $hDummy ; You can process here the responses from your functions by your will ConsoleWrite(GUICtrlRead($hDummy) & @CRLF) EndSwitch WEnd Func F1() GUICtrlSendToDummy($hDummy, 'I am F1: Here I can send some data...') EndFunc Func F2() GUICtrlSendToDummy($hDummy, 'I am F2: Here I can send some data...') EndFunc Func Quit() Exit EndFunc
Acanis Posted February 23, 2024 Author Posted February 23, 2024 (edited) Maybe Iam too tired and my description wasnt good, sorry...^^ I would like to know, if something like this (maybe not with an array, but its quick'n'dirty to show, what I try to achieve): expandcollapse popup#include <Array.au3> Global $taskQueue[0][2] ; [0] = Timestamp, [1] = Function AddTask(TimerInit() + 2500, "MyRepeatableFunction") AddTask(TimerInit() + 15000, "MyFunction") AddTask(TimerInit() + 20000, "MyFunction") AddTask(TimerInit() + 20000, "_ExitFunction") While Sleep(250) CheckTasks() WEnd Func MyFunction() ConsoleWrite("Task completed!" & @CRLF) EndFunc Func _ExitFunction() ConsoleWrite("Getting closed!" & @CRLF) Exit EndFunc Func MyRepeatableFunction() ConsoleWrite("Repeatable task completed!" & @CRLF) AddTask(TimerInit() + 2500, "MyRepeatableFunction") ; add the same function again EndFunc Func AddTask($interval, $function) Local $aTask[1][2] = [[$interval, $function]] _ArrayAdd($taskQueue, $aTask) _ArraySort($taskQueue) EndFunc Func CheckTasks() Local $currentTime = TimerInit() If UBound($taskQueue) > 0 Then If $currentTime >= $taskQueue[0][0] Then Call($taskQueue[0][1]) ; call the function _ArrayDelete($taskQueue, 0) ; delete the task EndIf EndIf EndFunc Its not working on the repeatable task and Iam not sure, if this direction is a "good" one... Like this I would have a global list with all the tasks and next timings. The code is now without GUI, because it is basically the same and so it is easier to show what I mean by "scheduled task queue". Edited February 23, 2024 by Acanis
Nine Posted February 23, 2024 Posted February 23, 2024 I do not think AutoIt is well suited to perform a task scheduler application. Since AutoIt is single thread, it would make it hard to follow the time sheet if multiple tasks are to be executed in parallel. You would want to run/call each task on separate thread and keep main thread looking for the next job to be executed. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
argumentum Posted February 23, 2024 Posted February 23, 2024 Try forking the work load. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
ioa747 Posted February 23, 2024 Posted February 23, 2024 (edited) if it serves you? expandcollapse popup#include <Array.au3> ; [0] = hTimer [1]Timestamp, [2] = Function Global $taskQueue[5][3] = [ _ [4, "Timestamp", "Function"], _ [0, 3000, "Task1"], _ [0, 5000, "Task2"], _ [0, 12000, "Task3"], _ [0, 60000, "_ExitFunction"]] While Sleep(250) CheckTasks() WEnd ;-------------------------------------------------------------------------------------------------------------------------------- Func _ExitFunction() ConsoleWrite("Getting closed!" & @CRLF) Exit EndFunc ;==>_ExitFunction ;-------------------------------------------------------------------------------------------------------------------------------- Func CheckTasks() For $i = 1 To $taskQueue[0][0] If $taskQueue[$i][0] = 0 Then $taskQueue[$i][0] = TimerInit() ContinueLoop EndIf If TimerDiff($taskQueue[$i][0]) > $taskQueue[$i][1] Then ConsoleWrite("call " & $taskQueue[$i][2] & @CRLF) ConsoleWrite(" time:" & Round(TimerDiff($taskQueue[$i][0]) / 1000, 3) & " seconds") Call($taskQueue[$i][2]) $taskQueue[$i][0] = TimerInit() EndIf Next EndFunc ;-------------------------------------------------------------------------------------------------------------------------------- Func Task1() ConsoleWrite("- Task1 completed!" & @CRLF) EndFunc ;==>MyFunction ;-------------------------------------------------------------------------------------------------------------------------------- Func Task2() ConsoleWrite("- Task2 completed!" & @CRLF) EndFunc ;==>MyFunction ;-------------------------------------------------------------------------------------------------------------------------------- Func Task3() ConsoleWrite("- Task3 completed!" & @CRLF) EndFunc ;==>MyFunction ;--------------------------------------------- Edit: It is certainly better, as the above said, to put each task on a separate thread expandcollapse popup#include <Array.au3> ; [0] = hTimer [1]Timestamp, [2] = Function, [3] = Parameter Global $taskQueue[5][4] = [ _ [4, "Timestamp", "Function", "Parameter"], _ [0, 3000, "Task1.au3", ""], _ [0, 5000, "Task2.au3", "-one -two"], _ [0, 8000, "Task3.au3", '-this "and this"'], _ [0, 60000, "Exit", ""]] While Sleep(250) CheckTasks() WEnd ;-------------------------------------------------------------------------------------------------------------------------------- Func _ExitFunction() ConsoleWrite("Getting closed!" & @CRLF) Exit EndFunc ;==>_ExitFunction ;-------------------------------------------------------------------------------------------------------------------------------- Func CheckTasks() For $i = 1 To $taskQueue[0][0] If $taskQueue[$i][0] = 0 Then $taskQueue[$i][0] = TimerInit() ContinueLoop EndIf If TimerDiff($taskQueue[$i][0]) > $taskQueue[$i][1] Then ConsoleWrite("call " & $taskQueue[$i][2] & @CRLF) ConsoleWrite(" time:" & Round(TimerDiff($taskQueue[$i][0]) / 1000, 3) & " seconds") If $taskQueue[$i][2] = "Exit" Then Exit RunTask(@ScriptDir & "\" & $taskQueue[$i][2], $taskQueue[$i][3]) $taskQueue[$i][0] = TimerInit() EndIf Next EndFunc ;==>CheckTasks ;-------------------------------------------------------------------------------------------------------------------------------- Func RunTask($FilePath, $Param = "") Run(FileGetShortName(@AutoItExe) & ' "' & FileGetShortName($FilePath) & ($Param = "" ? '"' : '" ' & $Param)) EndFunc ;==>RunTask ;-------------------------------------------------------------------------------------------------------------------------------- Edited February 23, 2024 by ioa747 add more I know that I know nothing
Acanis Posted February 23, 2024 Author Posted February 23, 2024 (edited) Thanks for your input. I'm a bit nervous about multithreading. I tried it out once and I found it very complicated to no longer clearly know the sequence of the executed functions. Sometimes they use the same data, read and write, and I have the feeling that it doesn't need that. I'm completely happy if a task only happens one after the other and then, for example, a notification/alarm is delayed by a few seconds. @ioa747 Thanks for the examples. I need to add tasks and delete them and Iam wondering what data type is makes the most sense for this. Arrays dont like to get changed its size, so maybe a map!? And I would like to have a sorted "queue" to debug. Maybe its even a good idea to work with dates, instead of timers?! Ill start to fuse your example with mine and then Ill try out a different data type and time format. *edit* Iam here now: expandcollapse popup#include <Array.au3> ; [0]Timestamp, [1] = Function, [2] = Parameter, [3] = Repeat, [4] = hTimer Global $taskQueue[0][5] AddTask(3000, "Task1") AddTask(5000, "Task2", "", True) AddTask(12000, "Task3") AddTask(20000, "_Exit") AddTask(12000, "TaskX", "param") While Sleep(250) CheckTasks() WEnd Func CheckTasks() For $i = UBound($taskQueue) -1 To 0 Step -1 If $taskQueue[$i][4] = 0 Then $taskQueue[$i][4] = TimerInit() ContinueLoop EndIf If TimerDiff($taskQueue[$i][4]) > $taskQueue[$i][0] Then ConsoleWrite("call " & $taskQueue[$i][1] & @CRLF) ConsoleWrite(" time:" & Round(TimerDiff($taskQueue[$i][4]) / 1000, 3) & " seconds") Call($taskQueue[$i][1], $taskQueue[$i][2] ? $taskQueue[$i][2] : "") If $taskQueue[$i][3] Then $taskQueue[$i][4] = TimerInit() Else _ArrayDelete($taskQueue, $i) EndIf EndIf Next EndFunc Func AddTask($iInterval, $sFunction, $vParameter = False, $bRepeat = False,$hTimer = 0) Local $aArray[1][5] = [[$iInterval, $sFunction, $vParameter, $bRepeat, $hTimer]] _ArrayAdd($taskQueue, $aArray) EndFunc Func _Exit() ConsoleWrite("Getting closed!" & @CRLF) Exit EndFunc ;==>_ExitFunction Func Task1() ConsoleWrite(": Task1 completed!" & @CRLF) EndFunc Func Task2() ConsoleWrite(": Task2 completed!" & @CRLF) EndFunc Func Task3() ConsoleWrite(": Task3 completed!" & @CRLF) EndFunc Func TaskX($iParam) ConsoleWrite(": TaskX completed with " & $iParam & "!" & @CRLF) EndFunc Edited February 23, 2024 by Acanis
Solution Acanis Posted February 23, 2024 Author Solution Posted February 23, 2024 (edited) Ok, I did add the calculation of the tasks target time and a function to show me the queue. Iam pretty happy with that state... Do you guys have ideas to improve it? Should I use another data type instead of an array? Do you see any weaknesses, apart from the fact that I deliberately do without multithreading? expandcollapse popup#include <Array.au3> #include <Date.au3> ENUM $TASK_INTERVAL, $TASK_FUNCTION, $TASK_PARAMETER, $TASK_REPEAT, $TASK_HTIMER, $TASK_DATE Global $taskQueue[0][6] AddTask(3, "Task1") AddTask(5, "Task2", "", True) AddTask(7, "Task_") DeleteTask("Task_") AddTask(12, "Task3") AddTask(20, "_Exit") AddTask(12, "TaskX", "param") OutputTaskQueue() While Sleep(250) CheckTasks() WEnd Func CheckTasks() For $i = UBound($taskQueue) -1 To 0 Step -1 If TimerDiff($taskQueue[$i][$TASK_HTIMER]) > $taskQueue[$i][$TASK_INTERVAL] Then ConsoleWrite("call " & $taskQueue[$i][$TASK_FUNCTION] & @CRLF) ConsoleWrite(" time:" & Round(TimerDiff($taskQueue[$i][$TASK_HTIMER]) / 1000, 3) & " seconds") Call($taskQueue[$i][$TASK_FUNCTION], $taskQueue[$i][$TASK_PARAMETER] ? $taskQueue[$i][$TASK_PARAMETER] : "") If $taskQueue[$i][$TASK_REPEAT] Then $taskQueue[$i][$TASK_HTIMER] = TimerInit() Else _ArrayDelete($taskQueue, $i) EndIf EndIf Next EndFunc Func AddTask($iInterval, $sFunction, $vParameter = False, $bRepeat = False, $hTimer = 0, $sDate = "") ; iInterval in Seconds $hTimer = $hTimer ? $hTimer : TimerInit() $iInterval *= 1000 ; from s to ms $sDate = _DateAdd('s', $iInterval / 1000, _NowCalc()) Local $aArray[1][6] = [[$iInterval, $sFunction, $vParameter, $bRepeat, $hTimer, $sDate]] _ArrayAdd($taskQueue, $aArray) EndFunc Func DeleteTask($sFunction) For $i = UBound($taskQueue) - 1 To 0 Step -1 If StringCompare($sFunction, $taskQueue[$i][$TASK_FUNCTION]) = 0 Then _ArrayDelete($taskQueue, $i) Next EndFunc Func _Exit() ConsoleWrite("Getting closed!" & @CRLF) Exit EndFunc ;==>_ExitFunction Func Task1() ConsoleWrite(": Task1 completed!" & @CRLF) EndFunc Func Task2() ConsoleWrite(": Task2 completed!" & @CRLF) EndFunc Func Task3() ConsoleWrite(": Task3 completed!" & @CRLF) EndFunc Func TaskX($iParam) ConsoleWrite(": TaskX completed with " & $iParam & "!" & @CRLF) EndFunc Func Task_() ConsoleWrite(": [ERROR] Task_ shouldnt be executed!" & @CRLF) EndFunc Func OutputTaskQueue() ; does not consider repetitive tasks ConsoleWrite("Task Queue:" & @CRLF) For $i = 0 To UBound($taskQueue) - 1 ConsoleWrite($taskQueue[$i][$TASK_DATE] & " | " & $taskQueue[$i][$TASK_FUNCTION] & @CRLF) Next ConsoleWrite(@CRLF) EndFunc *Edit* I added a DeleteTask-Function and an Enumn to make the array handling a little bit easier. *Edit_2* I changed the CheckTask, so its using the AddTask and DeleteTask. So $TASK_DATE gets calculated again for repeatable tasks. Looks a little bit messy now... ^^' expandcollapse popup#include <Array.au3> #include <Date.au3> ENUM $TASK_INTERVAL, $TASK_FUNCTION, $TASK_PARAMETER, $TASK_REPEAT, $TASK_HTIMER, $TASK_DATE Global $taskQueue[0][6] AddTask(3, "Task1") AddTask(5, "Task2", "", True) AddTask(7, "Task_") DeleteTask("Task_") AddTask(12, "Task3") AddTask(20, "_Exit") AddTask(12, "TaskX", "param") OutputTaskQueue() While Sleep(250) CheckTasks() WEnd Func CheckTasks() For $i = UBound($taskQueue) -1 To 0 Step -1 If TimerDiff($taskQueue[$i][$TASK_HTIMER]) > $taskQueue[$i][$TASK_INTERVAL] Then ConsoleWrite("call " & $taskQueue[$i][$TASK_FUNCTION] & @CRLF) ConsoleWrite(" time:" & Round(TimerDiff($taskQueue[$i][$TASK_HTIMER]) / 1000, 3) & " seconds") Call($taskQueue[$i][$TASK_FUNCTION], $taskQueue[$i][$TASK_PARAMETER] ? $taskQueue[$i][$TASK_PARAMETER] : "") Local $aTempTaskParams[4] = [$taskQueue[$i][$TASK_INTERVAL] / 1000, $taskQueue[$i][$TASK_FUNCTION], $taskQueue[$i][$TASK_PARAMETER], $taskQueue[$i][$TASK_REPEAT]] DeleteTask($i) If $taskQueue[$i][$TASK_REPEAT] Then AddTask($aTempTaskParams[0], $aTempTaskParams[1], $aTempTaskParams[2], $aTempTaskParams[3]) EndIf Next EndFunc Func AddTask($iInterval, $sFunction, $vParameter = False, $bRepeat = False, $hTimer = 0, $sDate = "") ; iInterval in Seconds $hTimer = $hTimer ? $hTimer : TimerInit() $iInterval *= 1000 ; from s to ms $sDate = _DateAdd('s', $iInterval / 1000, _NowCalc()) Local $aArray[1][6] = [[$iInterval, $sFunction, $vParameter, $bRepeat, $hTimer, $sDate]] _ArrayAdd($taskQueue, $aArray) _ArraySort($taskQueue, 0, 0, 0, $TASK_DATE) EndFunc Func DeleteTask($vDeleteTask) If IsInt($vDeleteTask) Then ; delete a special ID _ArrayDelete($taskQueue, $vDeleteTask) Else ; Delete all functions with that name For $i = UBound($taskQueue) - 1 To 0 Step -1 If StringCompare($vDeleteTask, $taskQueue[$i][$TASK_FUNCTION]) = 0 Then _ArrayDelete($taskQueue, $i) Next EndIf EndFunc Func _Exit() ConsoleWrite(": Getting closed!" & @CRLF) Exit EndFunc ;==>_ExitFunction Func Task1() ConsoleWrite(": Task1 completed!" & @CRLF) EndFunc Func Task2() ConsoleWrite(": Task2 completed!" & @CRLF) EndFunc Func Task3() ConsoleWrite(": Task3 completed!" & @CRLF) EndFunc Func TaskX($iParam) ConsoleWrite(": TaskX completed with " & $iParam & "!" & @CRLF) EndFunc Func Task_() ConsoleWrite(": [ERROR] Task_ shouldnt be executed!" & @CRLF) EndFunc Func OutputTaskQueue() ; does not consider repetitive tasks ConsoleWrite("Task Queue:" & @CRLF) For $i = 0 To UBound($taskQueue) - 1 ConsoleWrite($taskQueue[$i][$TASK_DATE] & " | " & $taskQueue[$i][$TASK_FUNCTION] & @CRLF) Next ConsoleWrite(@CRLF) EndFunc *Edit 3* Added the sorting into AddTask Edited February 24, 2024 by Acanis
argumentum Posted February 23, 2024 Posted February 23, 2024 3 hours ago, Acanis said: Func AddTask($iInterval, $sFunction, $vParameter = False, $bRepeat = False, $hTimer = 0, $sDate = "") ; iInterval in Seconds $hTimer = $hTimer ? $hTimer : TimerInit() ...just passing by. $hTimer = 0 ? TimerInit() : $hTimer is what you'd like to do. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Acanis Posted February 23, 2024 Author Posted February 23, 2024 (edited) 30 minutes ago, argumentum said: ...just passing by. $hTimer = 0 ? TimerInit() : $hTimer is what you'd like to do. Hmmm, there is no "check" if you write it like that?! The "checking part" is always "0". After I tried it anyway, it no longer worked. Edited February 23, 2024 by Acanis
argumentum Posted February 23, 2024 Posted February 23, 2024 ...didn't have time to read the code, just saw "$hTimer = $hTimer ? $hTimer" and to me it is always going to be $hTimer. Got to go. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now