如何使用依赖注入使共享资源线程安全? [英] How to make a shared resource thread-safe when using dependency injection?

查看:340
本文介绍了如何使用依赖注入使共享资源线程安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我想让我的应用程序在未来开源,但首先我想重构代码以使用最推荐的技术进行团队协作,以便其他开发人员能够更容易地更改我的源代码。



共享资源示例:在CFML语言中,您有Server范围,这是共享内存可用于任何



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


  1. 创建名为ServerMemoryManager的组件的单个实例,它提供用于写入和读取服务器范围的接口。

  2. 任何其他需要访问服务器范围的代码将通过init()函数或setServerMemoryManager()函数注入对ServerMemoryManager的单个实例的引用。

  3. 每当组件读/写数据到ServerMemoryManager对象时,将能够在内部锁定服务器范围,以便没有2个线程可以同时写入服务器范围内的同一块内存。

这是管理共享资源(共享内存,文件系统等)需要锁定才能是线程安全的最好方法吗?



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



编辑:基于接受的答案,而不是锁定scope =服务器,我将使用命名锁和管理共享资源与更细粒度的锁定。这可以允许使用多个对象来管理共享资源,假设它们都管理共享存储器中的不同密钥或文件系统中的文件。例如,一个应用程序可以有自己的唯一键或目录,以便它不会与尝试更改共享资源的另一个应用程序冲突。



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

 < cfcomponent> ; 
< cfscript>
variables.scope = false;
variables.scopeName = false;
< / cfscript>
< cffunction name =initaccess =publicoutput =noreturntype =scope>
< cfargument name =scopetype =structrequired =yes>
< cfargument name =scopeNametype =stringrequired =yes>
< cfscript>
variables.scope = arguments.scope;
variables.scopeName = arguments.scopeName;
return this;
< / cfscript>
< / cffunction>
< cffunction name =writeaccess =publicoutput =noreturntype =boolean>
< cfargument name =keytype =stringrequired =yes>
< cfargument name =valuetype =anyrequires =yes>
< cfargument name =timeouttype =numericrequired =nodefault =10>
< cftry>
< cflock type =exclusivename =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>

这样可以避免在整个应用程序中锁定,因为更新单个struct key是线程安全的。但是,当您在启动时构建大对象时,您需要确保它被锁定,直到该对象完全创建。



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

解决方案

CFLOCK只在每次出现都以相同的方式锁定时阻止代码执行。



例如:



page1.cfm

 < cflock type =exclusivescope =servertimeout =10> 
< cfset application.xyz ='abc'>
< / cflock>

page2.cfm


$ b b

 < cfset application.xyz ='123'> 

Page2.cfm将取消page1.cfm上的任何锁,如果page2同时运行page1。也就是说,你锁定在你的cfc里面是很好的,所以每个对象不必被锁定。



然而,锁定每一个事件是不够的。



page1.cfm

$ p> < cflock type =exclusivescope =servertimeout =10>
< cfset application.xyz ='abc'>
< / cflock>

page2.cfm


$ b b

 < cflock type =exclusivescope =servertimeout =10> 
< cfset application.xyz ='123'>
< / cflock>

这将暂停对page1和page2的每个请求的处理,但不会保护page1上的application.xyz从第2页上的application.xyz更改。为此,您需要为您的锁定提供名称 a>。


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



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


我相信 serverMemoryManagerObject 可能干扰 serverMemoryManagerObject2 ,除非你命名你的锁。



以下是有关锁定和禁止的更多信息




My current application is using single instance of an object as a global variable for many of the main components, which I understand is considered inferior to using dependency injection.

I wish to make my applications open source in the future, but first I want to refactor the code to use the most recommended techniques for team collaboration so that other developers will be able to change my source code more easily.

Example of a shared resource: In the CFML language, you have the Server scope, which is shared memory that is available to any request for the entire server instance.

Here is my new design concept for managing changes to the Server scope:

  1. Create a single instance of component named ServerMemoryManager which provides an interface for writing and reading to the server scope.
  2. Any other code that needs to access the server scope will be injected with a reference to the single instance of the ServerMemoryManager via an init() function or a setServerMemoryManager() function.
  3. Whenever a component reads/writes data to the ServerMemoryManager object, it will be able to internally lock the server scope so that no 2 threads can simultaneous write to the same piece of memory in the server scope.

Is this the best way to manage a shared resource (shared memory, filesystem, etc) that requires locking in order to be thread-safe?

Please describe any additional methods that can be used to manage a shared resource that requires locking during certain read/write operations which are considered best practices.

Edit: Based on the accepted answer, instead of locking scope="server", I will use named locks and manage the shared resources with more fine-grained locking. This may allow using multiple objects to manage the shared resources assuming they are all managing different keys in shared memory or files in the filesystem. For example, one application could have its own unique key or directory assigned to it so that it wouldn't conflict with another application trying to change a shared resource.

Edit2: I found I could use a single component named scope.cfc for each scope if I pass the scope into the init function when I create the object. I am now using fine-grained named locks. Let me know if it can be improved. The actual revised code now looks like this (I excluded the code for read, delete, clear). It also doesn't seem that it is required to have a single instance of the scope.cfc component anymore.

            <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:** I tested the performance of reading from server scope through a component method like this and found it to be 20 times slower then reading the server scope directly when using a read only lock and 4 times slower without a lock. The overhead of an extra function call hundreds or thousands of times per request will be too slow. Tests done on Railo 3.3.x.

I prefer to build a large object in a non-public request and then set a single shared memory scope key then try to write an incomplete object to the scopes. Example:

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

This lets you avoid locking across the entire application when you only write complete objects to shared memory since updating a single struct key is thread-safe. However, when you build the large object on startup, you need to be sure it is locked until that object is fully created.

I'm going to make the scope variable become directly readable by using the this scope instead of variables scope in the init function to avoid slowing down the application.

解决方案

CFLOCK only prevents code from executing if every occurrence is locked the same way.

For example:

page1.cfm

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

page2.cfm

<cfset application.xyz = '123'>

Page2.cfm is going to negate any locks you have on page1.cfm if page2 runs the same time page1 does. That said, it's good that you are locking inside your cfc so that each object doesn't have to be locked.

However, locking every occurrence isn't enough. The following won't do much good either.

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>

This will halt the processing for every request of page1 and page2 but will not protect application.xyz on page1 from changes made to application.xyz on page2. To do this you need to give your locks a "name".

Locks name. Mutually exclusive with the scope attribute. Only one request can execute the code within a cflocktag with a given name at a time. Cannot be an empty string.

Permits synchronizing access to resources from different parts of an application. Lock names are global to a ColdFusion server. They are shared among applications and user sessions, but not clustered servers.

Because you are creating multiple instances of your object, I believe serverMemoryManagerObject could interfere with serverMemoryManagerObject2 unless you name your lock.

Here is some more information about locking dos and don'ts

这篇关于如何使用依赖注入使共享资源线程安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆