ColdFusion :在单独的请求中终止 CFTHREAD



我在 ColdFusion 应用程序中使用 CFTHREAD。 从我从 Ben Nadel (https://www.bennadel.com/blog/2980-terminating-asynchronous-cfthreads-in-coldfusion.htm) 那里读到的内容来看,ColdFusion 仅公开和跟踪当前请求中的线程。 在我的情况下,我通过 ajax 调用生成一个线程,然后为用户提供一个取消按钮。 我希望取消按钮可以在线程上调用终止方法,但是无论我将其存储在何处(应用程序,服务器,会话),ColdFusion总是返回一个错误,指出它无法终止线程"THREAD_NAME",因为"THREAD_NAME"没有生成。

我知道在引擎盖下,ColdFusion主要是Java。 所以我希望可能有办法。 谁能证实或否认这种可能性? 有什么例子吗?

谢谢!

抱歉,我没有 50 的声誉可以评论,所以我会发布这个作为答案。 最近,我遇到了通过ajax生成的CFThread的相同情况,我需要以某种方式终止它,但无法做到。 我在 CFLoop 中有一个 CFQuery,它在应用程序范围内使用其数据源。 因此,我想出的是登录ColdFusion Administrator并暂时重命名导致线程引发数据库错误的数据源。 虽然这是不优雅的终止,但它达到了当时的目的。

因此,在看到这个问题后,如果没有已知的方法来解决此问题,它让我想到了可能的解决方法。 假设在线程处理期间,它会测试应用程序/服务器/会话范围内的变量值。 假设该值最初设置为"true",然后由另一个进程设置为"false",当线程找到 false 值时,它可以正常终止。

可以吗?

是的,但仅使用内部类。创建 cfthread 时,使用本地THREAD_NAME检索对基础线程对象的引用。

context = getPageContext().getFusionContext();
thread = context.getUserThreadTask( "theLocalTaskName" );

由于本地名称可由多个请求使用,因此引用应存储在唯一名称下,例如 uuid。该引用实际上是内部类coldfusion.threads.Task的实例。若要终止它,请调用其cancel()方法。

thread.cancel();

你应该吗?

这是一个大问题,完全取决于线程做什么 - 它是如何做到的 - 以及如果进程只是停止死,中游,没有警告,它使用的资源将如何受到影响。

原因是调用<cfthread action="terminate"..>会立即杀死线程。CF不在乎它是否在关键部分的中间。服务器只是用木槌敲打它并阻止它冷却。异常日志显示 CF 通过调用 Thread.stop() 来执行此操作

"Information","cfthread-47","09/07/19","17:10:44","","THREAD_V_2: Terminated"
java.lang.ThreadDeath
at java.base/java.lang.Thread.stop(Thread.java:942)
at coldfusion.thread.Task.cancel(Task.java:257)
at coldfusion.tagext.lang.ThreadTag.terminateThread(ThreadTag.java:345)
at coldfusion.tagext.lang.ThreadTag.doStartTag(ThreadTag.java:204)

java文档说stop()方法已被弃用,因为它本质上是不安全的:

停止线程会导致它解锁它拥有的所有显示器 锁。(监视器作为线程死亡异常解锁 向上传播堆栈。如果以前保护的任何对象 由于这些监视器处于不一致状态,其他线程现在可能 以不一致的状态查看这些对象。据说这样的物体 被损坏。当线程对损坏的对象进行操作时,任意 行为可能会导致。这种行为可能很微妙,很难 检测,或者可能发音。与其他未经检查的异常不同, 线程死亡无声地杀死线程;因此,用户没有警告 他的程序可能已损坏。腐败可以表现在 实际损坏发生后的任何时间,甚至数小时或数天 前途。

因此,重要的是要考虑线程的实际作用,并确定终止是否安全。例如,如果线程使用 FileOpen() 处理文件,则强制终止它可能会阻止线程释放句柄,从而使基础文件处于锁定状态,这是不可取的。

在 java 中停止线程的推荐方法是使用 interrupt()。这本质上就是 user12031119 描述的概念。中断不会强制终止线程。它只是一个标志,建议线程停止处理。由线程本身决定何时可以安全退出。这允许线程在终止之前完成关键部分或执行任何清理任务。是的,它需要更多的编码,但结果比"终止"更稳定和可预测。

您要做的是在应用程序或会话作用域之类的位置设置一个数据结构,以跟踪您希望能够取消的正在运行的线程。

Application.cfc OnApplicationStart

<cfset application.cancelThread = {} />

在输入线程之前创建 id,然后传递到线程中

<cfset threadId = createUUID() />
<cfset application.cancelThread[threadId] = false />

将 threadId 传递回客户端以获取取消按钮。单击取消按钮时,将线程 ID 传回

<cfset application.cancelThread[form.threadId] = true />

线程执行期间

<cfif application.cancelThread[threadId]>
<cfabort />
<!--- or your chosen approach to ending the processing --->
</cfif>

如果线程到达终点,则删除线程引用

<cfset structDelete(application.cancelThread, threadId) />

最新更新