我之前曾在cfm页面上询问有关cf范围的问题(很高兴我理解CFC范围和潜在问题),但我对变量范围仍然不清楚。
在上一个问题的答案中,有人建议使用cfm页面没有线程安全问题,并且您将无法获得两个不同用户访问同一页面并具有竞争条件或线程安全性问题的场景(即使我只是将我的变量保留在默认的cfm变量范围内,并且每个用户的变量范围将是独立且独立的(这是我的最后一个问题Coldfusion Scopes Clarification)
但是,我已经阅读了这篇关于在cfm页面上使用函数并使用变量范围的博客文章http://blog.alexkyprianou.com/2010/09/20/variables-scope-in-coldfusion/,这似乎暗示了一个场景,即变量范围在多个用户之间共享(我明白这一点CFC上下文中的问题 - 它们更类似于java类,变量范围是实例变量,如果CFC是共享/应用程序范围/单例,则会出现线程安全问题,但这似乎与先前的答案相反 - 如果变量放入在cfm页面上的函数变量范围可以被其他用户访问,那么肯定变量放在变量范围内直接在cfm页面代码中是一样的吗?
我希望有一些明确的文档和指南,但实际上并没有找到对不同范围及其可用位置的明确解释。
谢谢!
答案 0 :(得分:12)
Dan是正确的,问题中引用的博客文章是完全错误的。 Dan的代码演示了它,我有written-up and tested this thoroughly on my blog(它太大了,不能去这里)。
底线是CFM中的变量范围对这种竞争条件是安全的,因为每个请求的变量范围是不同的内存。因此一个variables.foo
与另一个variables.foo
不同,所以两者都不相交。
这同样适用于变量范围中的对象:它们的内部变量范围是一个独特的实体,因此任意数量的请求都可以在请求的变量范围中实例化CFC,而CFC实例的变量范围也都是离散实体。
变量范围唯一可以参与竞争条件的是存储在共享范围中的对象的变量范围。因为对该共享范围对象的所有引用都将引用内存中的同一对象,所以同一对象的变量范围在内存中。
答案 1 :(得分:4)
当2个请求运行代码时,访问变量作用域的CFC外部的函数不会出现线程安全问题,但是如果使用cfthread或其他并行功能,则仍可能存在变量范围被更改的问题,这可能会导致竞争条件。通常这个错误可能发生在你使用的变量很多,例如在for循环中,“i”变量。
有(ⅰ= 1; I< 10;我++){T = ARR [I]; }
但是当第一个函数运行时,另一个函数执行此操作:
有(ⅰ= 1; I< 20;我++){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等),否则它通常不会被破坏。线程安全错误仍然可能导致无限循环,这可能会挂起请求,直到超时为止,但除非内存不足,否则它不会崩溃。
答案 2 :(得分: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分钟内,打开另一个浏览器或选项卡并再次运行它。回到你第一次运行的地方并等待结果。如果两个输出之间没有显着差异,那么您的第二页请求不会影响您的第一个请求。
请注意,我自己没有尝试过,但我的赌注是第二次请求不会影响第一次。