3

我当前的应用程序使用对象的单个实例作为许多主要组件的全局变量,据我所知,这被认为不如使用依赖注入。

我希望将来让我的应用程序开源,但首先我想重构代码以使用最推荐的团队协作技术,以便其他开发人员能够更轻松地更改我的源代码。

共享资源示例:在 CFML 语言中,您拥有服务器范围,它是可用于整个服务器实例的任何请求的共享内存。

这是我用于管理服务器范围更改的新设计理念:

  1. 创建一个名为 ServerMemoryManager 的组件的单个实例,它提供了一个用于写入和读取服务器范围的接口。
  2. 需要访问服务器范围的任何其他代码都将通过 init() 函数或 setServerMemoryManager() 函数注入对 ServerMemoryManager 单个实例的引用。
  3. 每当一个组件读取/写入数据到 ServerMemoryManager 对象时,它将能够在内部锁定服务器范围,以便没有 2 个线程可以同时写入服务器范围内的同一块内存。

这是管理需要锁定以实现线程安全的共享资源(共享内存、文件系统等)的最佳方式吗?

请描述可用于管理在某些读/写操作期间需要锁定的共享资源的任何其他方法,这些方法被认为是最佳实践。

编辑:基于接受的答案,而不是锁定 scope="server",我将使用命名锁并使用更细粒度的锁定来管理共享资源。这可能允许使用多个对象来管理共享资源,假设它们都在管理共享内存中的不同键或文件系统中的文件。例如,一个应用程序可以分配给它自己的唯一键或目录,这样它就不会与另一个试图更改共享资源的应用程序发生冲突。

Edit2:我发现如果在创建对象时将范围传递给 init 函数,我可以为每个范围使用一个名为 scope.cfc 的单个组件。我现在使用细粒度的命名锁。让我知道它是否可以改进。实际修改后的代码现在看起来像这样(我排除了读取、删除、清除的代码)。似乎不再需要具有 scope.cfc 组件的单个实例。

            <cfcomponent>
                <cfscript>
                variables.scope=false;
                variables.scopeName=false;
                </cfscript>
                <cffunction name="init" access="public" output="no" returntype="scope">
                    <cfargument name="scope" type="struct" required="yes">
                    <cfargument name="scopeName" type="string" required="yes">
                    <cfscript>
                    variables.scope=arguments.scope;
                    variables.scopeName=arguments.scopeName;
                    return this;
                    </cfscript>
                </cffunction>
                <cffunction name="write" access="public" output="no" returntype="boolean">
                    <cfargument name="key" type="string" required="yes">
                    <cfargument name="value" type="any" requires="yes">
                    <cfargument name="timeout" type="numeric" required="no" default="10">
                    <cftry>
                        <cflock type="exclusive" name="zcore-#variables.scopeName#-scope-#arguments.key#" timeout="#arguments.timeout#" throwontimeout="yes">
                            <cfscript>
                            variables.scope[arguments.key]=arguments.value;
                            </cfscript>
                        </cflock>
                        <cfcatch type="lock"><cfreturn false></cfcatch>
                    </cftry>
                    <cfreturn true>
                </cffunction>
            </cfcomponent>

** Edit3:** 我通过这样的组件方法测试了从服务器范围读取的性能,发现它比使用只读锁时直接读取服务器范围要慢 20 倍,而没有锁时要慢 4 倍。每个请求数百或数千次额外函数调用的开销将太慢。在 Railo 3.3.x 上完成的测试。

我更喜欢在非公共请求中构建一个大对象,然后设置一个共享内存范围键,然后尝试将一个不完整的对象写入范围。例子:

<cfscript>
ts=structnew();
ts.largeObject=buildLargeObject();
server.cachedObject=ts;
</cfscript>

当您只将完整的对象写入共享内存时,这可以避免锁定整个应用程序,因为更新单个结构键是线程安全的。但是,当您在启动时构建大对象时,您需要确保它被锁定,直到该对象完全创建为止。

我将通过在 init 函数中使用 this 范围而不是变量范围来使范围变量变得直接可读,以避免减慢应用程序的速度。

4

1 回答 1

2

如果每次出现都以相同的方式锁定,CFLOCK 只会阻止代码执行。

例如:

page1.cfm

<cflock type="exclusive" scope="server" timeout="10" >
   <cfset application.xyz = 'abc'>
</cflock>

page2.cfm

<cfset application.xyz = '123'>

如果 page2 与 page1 的运行时间相同,那么 Page2.cfm 将取消您在 page1.cfm 上的任何锁定。也就是说,最好锁定在 cfc 中,这样就不必锁定每个对象。

然而,锁定每一个事件是不够的。以下也不会有多大好处。

page1.cfm

<cflock type="exclusive" scope="server" timeout="10" >
   <cfset application.xyz = 'abc'>
</cflock>

page2.cfm

<cflock type="exclusive" scope="server" timeout="10" >
   <cfset application.xyz = '123'>
</cflock>

这将停止对 page1 和 page2 的每个请求的处理,但不会保护 page1 上的 application.xyz 免受对 page2 上的 application.xyz 所做的更改。为此,您需要给您的锁起一个“名称”

锁名。与范围属性互斥。一次只有一个请求可以执行具有给定名称的 cflocktag 中的代码。不能为空字符串。

允许同步访问来自应用程序不同部分的资源。锁名称对于 ColdFusion 服务器是全局的。它们在应用程序和用户会话之间共享,但不是集群服务器。

因为您正在创建对象的多个实例,所以我相信除非您命名您的锁,否则serverMemoryManagerObject可能会干扰。serverMemoryManagerObject2

这是有关锁定注意事项的更多信息

于 2012-11-02T12:32:09.593 回答