You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AutoIt中非阻塞调用Log_Info函数避免主脚本阻塞

实现AutoIt日志库的非阻塞调用

AutoIt本身不支持原生异步函数调用,但可以通过以下两种可靠方式实现日志操作的非阻塞执行,避免API请求阻塞主脚本:


方案一:独立进程异步日志

将日志写入+时间戳请求逻辑拆分为独立脚本,主脚本通过启动独立进程执行日志操作,无需等待其完成。

步骤1:编写独立日志脚本(AsyncLogger.au3)

#include <Constants.au3>
#include <FileConstants.au3>
#include <File.au3>
#include <WinHttp.au3>

; 接收命令行参数:日志文件路径、日志级别、日志内容
If $CmdLine[0] < 3 Then Exit 1

Local $sLogFile = $CmdLine[1]
Local $sLogLevel = $CmdLine[2]
Local $sContent = $CmdLine[3]

; 原有时间戳获取函数
Func _Get_Timestamp($timezone = "Europe/Berlin", $timeout = 5000)
    Local $sServerName = "timeapi.io"
    Local $sObjectName = "/api/Time/current/zone?timeZone=" & $timezone

    Local $hSession = _WinHttpOpen()
    If @error Then Return "TIMESTAMP NOT AVAILABLE"

    Local $hConnect = _WinHttpConnect($hSession, $sServerName)
    If @error Then
        _WinHttpCloseHandle($hSession)
        Return "TIMESTAMP NOT AVAILABLE"
    EndIf

    Local $hRequest = _WinHttpOpenRequest($hConnect, "GET", $sObjectName)
    If @error Then
        _WinHttpCloseHandle($hConnect)
        _WinHttpCloseHandle($hSession)
        Return "TIMESTAMP NOT AVAILABLE"
    EndIf

    _WinHttpSetTimeouts($hRequest, $timeout, $timeout, $timeout, $timeout)

    If Not _WinHttpSendRequest($hRequest) Then
        _WinHttpCloseHandle($hRequest)
        _WinHttpCloseHandle($hConnect)
        _WinHttpCloseHandle($hSession)
        Return "TIMESTAMP NOT AVAILABLE"
    EndIf

    If Not _WinHttpReceiveResponse($hRequest) Then
        _WinHttpCloseHandle($hRequest)
        _WinHttpCloseHandle($hConnect)
        _WinHttpCloseHandle($hSession)
        Return "TIMESTAMP NOT AVAILABLE"
    EndIf

    Local $sResponse = "", $sBuffer
    While 1
        $sBuffer = _WinHttpReadData($hRequest)
        If @error Or $sBuffer = "" Then ExitLoop
        $sResponse &= $sBuffer
    WEnd

    _WinHttpCloseHandle($hRequest)
    _WinHttpCloseHandle($hConnect)
    _WinHttpCloseHandle($hSession)

    Local $aMatch = StringRegExp($sResponse, '"dateTime"\s*:\s*"([^"]+)"', 1)
    If @error Or UBound($aMatch) = 0 Then Return "TIMESTAMP NOT AVAILABLE"

    Local $sDateTime = StringReplace(Stringleft($aMatch[0],19), "T", " ")
    Return $sDateTime = "" ? "TIMESTAMP NOT AVAILABLE" : $sDateTime
EndFunc

; 日志写入逻辑
Func _Write_Log($log_file, $content, $level)
    Local $sTimestamp = _Get_Timestamp()
    Local $sLogLine = "[" & $sTimestamp & "] [" & $level & "] " & $content & @CRLF
    
    ; 确保日志目录存在
    Local $sDir = StringLeft($log_file, StringInStr($log_file, "\", 0, -1))
    If Not FileExists($sDir) Then DirCreate($sDir)
    
    ; 写入日志
    Local $hFile = FileOpen($log_file, $FO_APPEND)
    If $hFile = -1 Then Return False
    FileWrite($hFile, $sLogLine)
    FileClose($hFile)
    Return True
EndFunc

; 执行日志写入并退出
_Write_Log($sLogFile, $sContent, $sLogLevel)
Exit 0

步骤2:主脚本修改Log_Info为启动独立进程

Func Log_Info($log_file, $content)
    ; 转义特殊字符避免命令行解析错误
    Local $sEscapedLogFile = StringReplace($log_file, '"', '""')
    Local $sEscapedContent = StringReplace($content, '"', '""')
    ; 启动独立日志进程,不等待完成
    Run('"' & @AutoItExe & '" /AutoIt3ExecuteScript "AsyncLogger.au3" "' & $sEscapedLogFile & '" "INFO" "' & $sEscapedContent & '"')
    Return True
EndFunc

; 原有调用逻辑保持不变
Func _RunCatch($program, $report = true)
    Local $pid = Run($program)
    Log_Info(Get_Logfile(), "Test wird ausgeführt: " & $program)
    if $report And @error > 0 Then
        MsgBox($MB_OK + $MB_ICONWARNING, "_RunCatch", "Der Test konnte nicht gestartet werden." & @CRLF & "Fehlermeldung: " & _WinAPI_GetErrorMessage(@error))
    EndIf
EndFunc ;==> _RunCatch()

方案二:队列+定时任务异步处理

通过全局队列缓存日志请求,用AdlibRegister注册定时任务,后台批量处理日志写入。

主脚本修改实现

#include <Constants.au3>
#include <FileConstants.au3>
#include <File.au3>
#include <WinHttp.au3>

; 全局日志队列
Global $g_aLogQueue = []

; 注册定时处理任务,每500ms检查一次队列
AdlibRegister("_Process_Log_Queue", 500)

; 非阻塞日志入队函数
Func Log_Info($log_file, $content)
    _ArrayAdd($g_aLogQueue, [$log_file, $content, "INFO"])
    Return True
EndFunc

; 定时处理队列的后台函数
Func _Process_Log_Queue()
    If UBound($g_aLogQueue) = 0 Then Return
    
    ; 取出队列首个日志条目
    Local $aLogEntry = $g_aLogQueue[0]
    _ArrayDelete($g_aLogQueue, 0)
    
    ; 执行原有日志写入逻辑
    _Write_Log($aLogEntry[0], $aLogEntry[1], $aLogEntry[2])
EndFunc

; 保留原有_Get_Timestamp和_Write_Log函数
Func _Get_Timestamp($timezone = "Europe/Berlin", $timeout = 5000)
    ; 原函数代码不变
EndFunc

Func _Write_Log($log_file, $content, $level)
    ; 原函数代码不变
EndFunc

; 原有调用逻辑保持不变
Func _RunCatch($program, $report = true)
    Local $pid = Run($program)
    Log_Info(Get_Logfile(), "Test wird ausgeführt: " & $program)
    if $report And @error > 0 Then
        MsgBox($MB_OK + $MB_ICONWARNING, "_RunCatch", "Der Test konnte nicht gestartet werden." & @CRLF & "Fehlermeldung: " & _WinAPI_GetErrorMessage(@error))
    EndIf
EndFunc ;==> _RunCatch()

额外优化建议

  • 缩短API超时时间:将_Get_Timestamp的超时从5秒改为2秒,减少资源占用
  • 缓存时间戳:短时间内复用上次获取的时间戳(比如1分钟内),减少API请求次数
  • 本地时钟降级:API请求失败时,优先使用本地系统时间,避免日志缺失

内容的提问来源于stack exchange,提问作者Doppelbemme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 15:54:53