如何在使用依赖注入时使共享资源成为线程安全的?

时间:2012-11-02 06:10:59

标签: multithreading coldfusion dependency-injection thread-safety locking

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

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

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

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

  1. 创建名为ServerMemoryManager的组件的单个实例,该实例提供用于写入和读取服务器范围的接口。
  2. 需要访问服务器作用域的任何其他代码将通过init()函数或setServerMemoryManager()函数注入对ServerMemoryManager的单个实例的引用。
  3. 每当组件读取/写入ServerMemoryManager对象的数据时,它就能够在内部锁定服务器范围,这样就不会有2个线程同时写入服务器范围内的同一块内存。
  4. 这是管理需要锁定以便线程安全的共享资源(共享内存,文件系统等)的最佳方法吗?

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

    编辑:根据接受的答案,我将使用命名锁并使用更精细的锁定来管理共享资源,而不是锁定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 scope而不是变量scope来使scope变量直接可读,以避免减慢应用程序的速度。

1 个答案:

答案 0 :(得分: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的每个请求的处理,但不会保护第1页上的application.xyz不受第2页上对application.xyz所做的更改的影响。为此,您需要give your locks a "name"

  

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

     

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

由于您要创建对象的多个实例,因此我认为serverMemoryManagerObject可能会干扰serverMemoryManagerObject2,除非您为锁定名称。

以下是有关锁定注意事项和注意事项的更多信息