Lucee/Adobe ColdFusion - 从线程设置/获取信息{}

Gri*_*ari 2 coldfusion cfthread cfloop lucee

我正在创建一个简单的电子邮件服务器状态页面,该页面调用两个不同的 CFC。

状态页要求:

  1. 通过 CFC 查询 MariaDB 数据库表并从两个字段返回数据:server_name(即 MyServerName)和 server_domain(即 mail.domain.com)。目前,数据库表中有 4 行需要提取。
  2. 将步骤 1 中的数据库数据传递给 CFC,检查端口 25 是否正在侦听。如果 CFC 可以到达端口 25,则结果为 true,否则结果为 false。这一步需要进行线程化。
  3. 将步骤 2 中的布尔结果传递给循环以打印 server_name 和布尔结果。

输出类似于以下内容:
MyServerName - <up arrow>
MyServerName2 - <up arrow>
MyServerName3 - <up arrow>
MyServerName4 -<down arrow>

代码:

    RetrieveEmailServers = APPLICATION.selectQueries.RetrieveEmailServers()
    if (RetrieveEmailServers.recordCount) {
        for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
            LOCAL.theDomains = RetrieveEmailServers.check_servers_domain[i];
            LOCAL.theNames = RetrieveEmailServers.check_servers_name[i];
            thread action="run" name="thread#i#" theDomains="#LOCAL.theDomains#" theNames="#LOCAL.theNames#" {
                VARIABLES.theServers = APPLICATION.emailCheck.checkSMTPServer('#domains#',25,'','');
            }
        }
        thread action="join" timeout="6000"{}

        for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
            VARIABLES.theResult = cfthread["thread#i#"];
            if (VARIABLES.theResult.theServers) {
                LOCAL.theStatus = "<i class='fad fa-angle-double-up text-success fs-1'></i>"
            }
            else {
                LOCAL.theStatus = "<i class='fad fa-angle-double-down text-danger fs-1'></i>"
            } 
            writeOutput(ATTRIBUTES.theNames & " - " & LOCAL.theStatus & "<br>");
        }
    }
    else {
        writeOutput("No servers listed at this time.")
    }
Run Code Online (Sandbox Code Playgroud)

错误:键[THESERVERS]不存在,结构为空

审议:

  1. 我知道我的代码不是很好,而且我知道它可以写得更好。我正在努力改进。
  2. 我不是一名全职编码员,但多年来我一直断断续续地编码。我仍然认为自己是 CFML 的新手,因此很多方法都超出了我的理解范围。
  3. 上面的代码大部分都有效,但我很难理解如何在 CFTHREAD 之外传递信息以在页面的其余部分中使用,特别是在处理 CFLOOP 时。
  4. 我已经读了很多遍,但仍然不完全理解如何正确使用线程局部作用域、线程作用域和属性作用域。
  5. 上面的代码有一个简单的任务,检查端口,但最终目标是对我的应用程序的其他部分使用类似的代码。我知道有更好的监控工具可用;这是一个帮助我理解和学习的练习。
  6. 具体到 Lucee,我知道threadData()['thread#i#'].status;或类似可能是对cfthread[].

Age*_*eax 5

属性

Attributes范围仅用于保存传递线程中的值。因此,作用域是短暂的,仅存在于线程内。每个线程都有自己的“属性”范围,该范围在该线程运行之前或完成之后不存在。

例如,此代码片段传入名为“theDomains”的属性。该变量Attributes.theDomains只存在于线程内部。

 thread action="run" name="thread1" theDomains="example.com" {
     writeDump( attributes.theDomains );
 }
 
 thread action="join" name="thread1" {};
 
 writeOutput( thread1.output );
Run Code Online (Sandbox Code Playgroud)

线程本地

“线程局部”是另一个短暂的作用域,其目的是保存在线程内使用的变量。每个线程都有自己的私有“本地”范围,与所有其他线程分开。与attributes作用域一样,它仅在线程执行时存在,并在线程完成时被清除。

例如,此代码片段创建一个名为“MyLocalVar”的局部变量。显示线程output表明该变量存在于线程中

thread action="run" name="thread1" {
    // Un-scoped v
    myLocalVar = "foo";
    writeOutput( "myLocalVar ="& myLocalVar );
}

thread action="join" name="thread1" {};
writeOutput( thread1.output );
Run Code Online (Sandbox Code Playgroud)

但是在线程完成后尝试访问它会导致错误

// fails with error "key [MYLOCALVAR] doesn't exist"
writeOutput( "myLocalVar ="& thread1.myLocalVar );
Run Code Online (Sandbox Code Playgroud)

Thread范围

瞄准镜Thread的使用寿命更长。它旨在存储“..线程特定的变量和有关线程的元数据......”。更重要的是,该作用域可用于将信息传递回调用页面(甚至其他线程)。

例如,此代码片段创建一个线程范围的变量,即使在线程完成其执行之后,该变量对调用页面也是可见的:

 thread action="run" name="thread1" {
    // use scope prefix "thread."
    thread.myThreadVar = "foo";
 }
 
 thread action="join" name="thread1" {};
 
 writeOutput( "thread1.myThreadVar="& thread1.myThreadVar );
 writeDump( thread1 );
Run Code Online (Sandbox Code Playgroud)

问题:键 [THESERVERS] 不存在

当您花了几天时间查看错误时,很容易忘记基础知识:) 处理未定义错误的第一件事是转储对象,并在尝试使用它之前查看是否确实包含您期望的内容。

for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
    VARIABLES.theResult = cfthread["thread#i#"];
    writeDump( variables.theResult );

    /* temporarily comment out rest of code 
    ...
    */
}
Run Code Online (Sandbox Code Playgroud)

转储VARIABLES.theResult显示线程实际上因不同的错误而失败

线程 1 的 CFDump

由于线程内的属性名称错误。应该是attributes.theDomains,不是domains

thread ...{
    APPLICATION.emailCheck.checkSMTPServer( attributes.theDomains, ... );
}
Run Code Online (Sandbox Code Playgroud)

好的,我修好了。仍然收到“密钥 [THESERVERS] 不存在”,现在怎么办?

另一个线程转储显示错误消息并没有说谎。theServers由于作用域不正确,线程实际上不包含名为 的变量。使用thread范围,而不是“变量”。

 thread ...{
     thread.theServers = ....;
 }
Run Code Online (Sandbox Code Playgroud)

线程 2 的 CFDump

又一个错误?!变量 [ATTRIBUTES] 不存在

即使修复了前两个问题后,您仍然会在这里收到另一个错误

for(i = 1; i <= RetrieveEmailServers.recordCount(); i++) {
   ...
   writeOutput(ATTRIBUTES.theNames & " - " & LOCAL.theStatus & "<br>");
}
Run Code Online (Sandbox Code Playgroud)

请记住,attributes作用域仅存在于线程内。所以显然一旦线程完成就不能使用它。也可以存储theNamesthread范围中,或者由于您正在循环查询,因此使用查询列值RetrieveEmailServers.the_query_column_name[ i ]

连接线程

最后一个潜在问题。join 语句实际上并不等待您创建的线程。它只是等待 6000 毫秒。如果由于某种原因任何线程花费的时间超过该时间,您在尝试检索线程结果时将会收到错误。要实际等待创建的线程,您必须使用thread action="join" name=(list of thread names) {}. 我将把它作为读者的练习(:

说实话,还有其他事情可以清理和/或改进,但希望这个冗长的杂乱线程解释了为什么首先发生错误以及如何在将来使用线程时避免它们