【问题标题】:How to make a shared resource thread-safe when using dependency injection?使用依赖注入时如何使共享资源线程安全?
【发布时间】:2012-10-22 19:34:34
【问题描述】:

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

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

共享资源的示例:在 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 范围而不是 variables 范围来使范围变量变得直接可读,以避免降低应用程序的速度。

【问题讨论】:

  • 您能否澄清第 3 步。锁定是否由 ServerMemoryManager 内部处理,或者该组件的用户是否需要在数据读取/写入之前调用组件上的方法来锁定?我希望锁是内部管理的。
  • @Brady 锁将在 ServerMemoryManager 组件内部进行管理。理想情况下,我会将与共享资源相关的所有内容都封装在最简单的公共接口中。
  • 听起来不错的设计。

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


【解决方案1】:

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 所做的更改。为此,您需要give your locks a "name"

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

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

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

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

【讨论】:

  • 您混合了服务器和应用程序范围,这会破坏锁定 - 这是一个错误吗?有多个对象依赖于上面的 1 个 serverMemoryManagerObject 实例。 cflock scope="server" 将安全地锁定服务器范围,而 cflock scope="application" 将安全地锁定应用程序范围,所以我不确定您要告诉我什么,否则我应该尝试以更细粒度的方式锁定避免过多地阻止其他请求 - 我会这样做。我还打算为每个共享资源创建一个单独的组件。即用于应用程序、会话、文件系统等
  • 我不确定你所说的混合范围是什么意思会破坏锁定。来自文档:“cflock 标签防止同时访问代码段,而不是变量。”我以设置应用程序范围变量为例,但我可以简单地说“我不希望我的服务器上的任何人同时访问{此共享的 cfc/自定义标签,该标签将文件读/写到共享文件夹} "
  • 我将 cflock 范围属性解释为它锁定了嵌套的服务器范围结构,但它没有。你是对的,多年来我一直无法正确理解 cflock,但我的应用程序大部分时间都在工作,因为我锁定了需要锁定的部分。将来我将不得不重新考虑如何使用 cflock。我想我将有单独的组件来处理服务器范围内的单独键,并使用命名锁而不是 cflock scope="server"。我没有意识到我在想这个错误。我会把你标记为答案。谢谢。
  • 容易混淆。也许如果该属性被称为环境或范围或限制等其他东西......阅读我发布的四个链接,那里有很多好东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-27
  • 2019-10-18
  • 2010-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多