9

我之前在 cfm 页面上问过一个关于 cf 范围的问题(很高兴我了解 CFC 范围和潜在问题),但我仍然不清楚变量范围。

在我上一个问题的答案中,有人建议使用 cfm 页面不存在线程安全问题,并且您不会遇到两个不同用户访问同一页面并存在竞争条件或线程安全问题的情况(即使我只需将我的变量留在默认的 cfm 变量范围内,每个用户的变量范围将是隔离和独​​立的(这是我的最后一个问题Coldfusion Scopes Clarification

但是,我已经阅读了这篇博客文章http://blog.alexkyprianou.com/2010/09/20/variables-scope-in​​-coldfusion / 关于在 cfm 页面上使用函数和使用变量范围,这似乎建议在多个用户之间共享变量范围的场景(我在 CFC 的上下文中理解这个问题 - 它们更类似于 java 类并且变量范围是实例变量,因此如果 CFC 是共享的/应用程序,则会出现线程安全问题范围/单例),但这似乎与以前的答案相反 - 如果其他用户可以访问由 cfm 页面上的函数放入变量范围的变量,那么直接在 cfm 页面代码中放置在变量范围中的变量肯定是相同的吗?

我希望有一些清晰的文档和指南,但还没有真正找到不同范围的明确解释以及它们在哪里可用。

谢谢!

4

3 回答 3

12

丹是正确的,问题中引用的博客文章是完全错误的。Dan 的代码演示了这一点,我已经在我的博客上对此进行了彻底的编写和测试(它太大了,不能去这里)。

底线是 CFM 中的变量范围不受这种竞争条件的影响,因为每个请求的变量范围是不同的内存。所以一个variables.foo与另一个不同variables.foo,所以两者都不相交。

这同样适用于变量范围内的对象:它们的内部变量范围是一个不同的实体,因此任何数量的请求都可以在请求的变量范围内实例化一个 CFC,而 CFC 实例的变量范围也是离散的实体。

变量作用域唯一可以参与竞争条件的是存储在共享作用域中的对象的变量作用域。因为对该共享范围对象的所有引用都将引用内存中的同一对象,所以同一对象的变量范围在内存中。

于 2013-04-06T21:47:00.720 回答
4

当 2 个请求运行代码时,访问变量范围的 CFC 之外的函数不会出现线程安全问题,但是如果您使用 cfthread 或其他并行功能,您仍然可能会遇到变量范围被更改的问题,这可能会导致竞争条件. 通常,这个错误可能发生在你经常使用的变量上,比如在 for 循环中,“i”变量。

for(i=1;i<10;i++){t=arr[i]; }

但随后另一个函数在第一个函数运行时执行此操作:

for(i=1;i<20;i++){t=arr[i]; }

“i”变量需要成为局部变量以帮助使其成为线程安全的。您不希望第一个循环能够错误地超过 10,这很难多次调试。当我开始缓存对象并更广泛地使用 cfthread 时,我必须修复大量“i”变量和其他变量,以使我的函数在任何地方都是线程安全的。

您还可以通过从不更改现有对象来避免需要锁定。您可以改为在它们的副本上工作。这使得数据“不可变”。CFML 没有官方支持更有效地制作不可变对象,但您可以轻松制作副本。 http://en.wikipedia.org/wiki/Immutable_object

对应用程序范围变量进行线程安全更改的简单示例:

var temp=structnew(); 
// build complete object
temp.myValue=true; 
// set complete object to application scope variable
application.myObject=temp;  

写入任何共享对象通常是危险的,因为变量可能未定义或部分构造。我总是构造完整的对象,并在最后将其设置为共享变量,就像上面的示例一样。如果重新创建数据的成本不会太高,这会使线程安全变得容易。CFC 中的变量作用域类似于其他语言中的私有成员变量。如果您修改共享对象中的数据,如果您不能制作副本,您可能会使用 CFLOCK。

关于 Coldfusion 范围的一些混淆与 Coldfusion 5 和更早版本中的共享范围不太可靠有关。他们有严重的线程安全问题,可能导致数据损坏或崩溃。如果您没有正确锁定,两个线程在某些条件下能够同时写入同一内​​存。当前的 CFML 引擎能够写入结构键而不会损坏/崩溃。现在,如果不考虑线程安全,您只是无法确定哪些数据实际上最终会作为值,但它通常不会损坏,除非您处理非 cfml 对象类型,如 CFX、Java 和其他. 线程安全错误仍然可能导致无限循环,该循环可能会挂起请求直到超时,但除非内存不足,否则它不应该崩溃。

于 2013-04-07T00:53:32.817 回答
3

我认为该博客具有误导性。但是,如果您想自己看,请用他的功能写一个页面。让它看起来像这样。

<cffunction name="test" returntype="void">
<cfscript>
foo = now();
sleep(3 * 60 * 1000);  // should be 3 minutes
writedump(foo);
</cfscript>
<cffunction>

<cfdump var="#now()#">
<cfset test()>

运行页面。在 3 分钟内,打开另一个浏览器或选项卡并再次运行。回到你第一次运行它的地方并等待结果。如果两个输出之间没有显着差异,那么您的第二个页面请求不会影响您的第一个。

请注意,我自己没有尝试过,但我的赌注是第二个请求不会影响第一个请求。

于 2013-04-06T20:26:15.927 回答