在同一请求中创建和更新结构时可能出现的竞争条件 - Coldfusion

2024-03-18

大约一年前,我问了一个关于我在应用程序中遇到的错误的问题,该错误表明可能存在竞争条件:

在 ColdFusion 中创建结构体时可能存在竞争条件 https://stackoverflow.com/questions/19859690/possible-race-condition-creating-structs-in-coldfusion

一年过去了,我仍然遇到这个应用程序和其他使用相同技术的应用程序的问题,坦率地说,您似乎无法在同一请求中可靠地创建和更新结构。 这当然看起来很可笑,所以我一定做错了什么 - 但我会很感激一些帮助。

这是对正在发生的事情的解释:

  • 我有一个在应用程序范围中实例化的函数,它接受逗号分隔的 ID 列表作为参数。
  • 该函数访问数据库,请求具有匹配 ID 的所有记录
  • 然后,我们循环遍历记录集,并创建一个本地范围的结构,使用记录的 ID 动态命名,例如Local.myStruct['item_' & myrecordset.ID]
  • 最后我们将数据添加到这个结构中。

问题是,代码会间歇性地在最后一步出错,指出前两行创建的动态命名结构不存在......有趣的是,有问题的代码行正在更新结构,并且所以如果它不存在,我希望它简单地创建它 - 我想知道使用数组表示法设置动态命名变量的值是否与使用点表示法的行为不同?

所以,这里有一些代码:

<cffunction name="GetPrices" access="public" returntype="struct">
    <cfargument name="ItemID"       type="string"   required="true" />
    <cfargument name="PriceBand"    type="string"   default="1"     />

    <cfset var Local = {} />

    <!--- Query the database for the part details --->
    <cfquery name="Local.GetParts" datasource="#this.sDSN#">
        SELECT  TOP 200
                [ItemID]
            ,   [Price1]
            ,   [Price2]
            ,   [Price3]
        FROM    [Items]

        WHERE   1 = 1

        <cfif ListLen(Arguments.ItemID) GT 1>
            AND ItemID IN (<cfqueryparam cfsqltype="cf_sql_integer" list="yes" value="#Arguments.ItemID#">)
        <cfelse>
            AND itemID = <cfqueryparam cfsqltype="cf_sql_integer" value="#Arguments.ItemID#">
        </cfif>
    </cfquery>

    <cfloop query="Local.GetParts">
        <cfset Local.ReturnStruct['item_' & Local.GetParts.ItemID] = {} />

        <cfset Local.ReturnStruct['item_' & Local.GetParts.itemID].CurrentPrice = Local.GetParts['price' & Arguments.PriceBand] />
    </cfloop>

    <cfreturn Local.ReturnStruct />

</cffunction>

该函数将这样调用:

Variables.GetPrices = Application.com.Items.GetPrices(
        ItemID      = '8263,1996,324686,32,12746,297807,1763,37568,2359782,321,3525,563466,323'
    ,   PriceBand   = 2
)

正如您所看到的,该函数是 varscoped 的,并且在任何情况下我们都运行 CF10,我相信隐式 Local 作用域在函数中是线程安全的。 Var 范围是我们去年解决这个问题的地方,但可惜问题仍然存在。有人可以提供其他建议吗?

Thanks

EDIT:根据要求,这是我收到的错误的示例:

元素 item_61284 在作为表达式一部分引用的 CFML 结构中未定义。

每次的项目 ID 都不同。可能值得注意的是,出于问题的目的,发布的代码已被稍微操纵 - 因此下面的堆栈跟踪可能会显示无法归因于上面代码的零碎内容。

我确信从生产代码中删除的任何内容都与该问题无关,因为无论使用相同的技术,这些症状都会出现在多个应用程序的数十个功能中。

Coldfusion.runtime.UndefinedElementException:元素 item_61284 在作为表达式一部分引用的 CFML 结构中未定义。在coldfusion.runtime.CfJspPage.ArrayGetAt(CfJspPage.java:974)在coldfusion.runtime.CfJspPage._arrayGetAt(CfJspPage.java:985)在coldfusion.runtime.CfJspPage._arrayGetAt(CfJspPage.java:980)在cfparts2ecfc1041715109$funcREDACTED。 runFunction(REDACTED.cfc:665)在coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472)在coldfusion.runtime.UDFMethod $ ReturnTypeFilter.invoke(UDFMethod.java:405)在coldfusion.runtime.UDFMethod $ ArgumentCollectionFilter.invoke (UDFMethod.java:368)在coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55)在coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:321)在coldfusion.runtime.UDFMethod.invoke(UDFMethod.java: 518)在coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:660)在coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:469)在coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2373)在REDACTEDcfm969331649。 runPage(REDACTED.cfm:43)在coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:244)在coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:444)在coldfusion.runtime.CfJspPage._emptyTcfTag(CfJspPage) .java:2799)在REDACTEDcfm522717774.runPage(REDACTED.cfm:83)在coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:244)在coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:444)在coldfusion .runtime.CfJspPage._emptyTcfTag(CfJspPage.java:2799)在cfapplication2ecfc1345221948$funcONREQUEST.runFunction(REDACTED\Application.cfc:573)在coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:472)在coldfusion.runtime.UDFMethod$ ReturnTypeFilter.invoke(UDFMethod.java:405)在coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:368)在coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:55)在coldfusion.runtime.UDFMethod.runFilterChain (UDFMethod.java:321)在coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:220)在coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:655)在coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java: 444)在coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:414)在coldfusion.runtime.AppEventInvoker.invoke(AppEventInvoker.java:108)在coldfusion.runtime.AppEventInvoker.onRequest(AppEventInvoker.java:300)在coldfusion。 filter.ApplicationFilter.invoke(ApplicationFilter.java:424)在coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:48)在coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)在coldfusion.filter.PathFilter.invoke (PathFilter.java:112)在coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94)在coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)在coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java: 38)在coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:46)在coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)在coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)在coldfusion。 filter.CachingFilter.invoke(CachingFilter.java:62) 在 Coldfusion.CfmServlet.service(CfmServlet.java:219) 在 Coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89) 在 org.apache.catalina.core.ApplicationFilterChain .internalDoFilter(ApplicationFilterChain.java:305)在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)在coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)在coldfusion.bootstrap。 BootstrapFilter.doFilter(BootstrapFilter.java:46) 在 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 在 org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 在太阳.reflect.GenerateMethodAccessor63.invoke(未知来源) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(未知来源) 在 java.lang.reflect.Method.invoke(未知来源) 在 com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter (WrappedFilterChain.java:97) 在 com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doNext(FusionReactorRequestHandler.java:437) 在 com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doHttpServletRequest(FusionReactorRequestHandler.java:311) 在com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doFusionRequest(FusionReactorRequestHandler.java:192)在com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.handle(FusionReactorRequestHandler.java:472)在com.intergral.fusionreactor.j2ee。 filter.FusionReactorCoreFilter.doFilter(FusionReactorCoreFilter.java:36) 在 sun.reflect.GenerateMethodAccessor61.invoke(未知来源) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(未知来源) 在 java.lang.reflect.Method.invoke(未知来源)在com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter(WrappedFilterChain.java:79)在sun.reflect.GenerateMethodAccessor60.invoke(未知来源)在sun.reflect.DelegatingMethodAccessorImpl.invoke(未知来源)在java.lang。 com.intergral.fusionreactor.agent.filter.FusionReactorStaticFilter.doFilter(FusionReactorStaticFilter.java:53)处的reflect.Method.invoke(未知来源) com.intergral.fusionreactor.agent.pointcuts.NewFilterChainPointCut$1.invoke(NewFilterChainPointCut.java: 41)在org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java)在org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)在org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:169)在org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)在org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)在org.apache。 catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) 在 org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:928) 在 org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java: 118) 在 org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:414) 在 org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:204) 在 org.apache.coyote.AbstractProtocol$AbstractConnectionHandler .process(AbstractProtocol.java:539) 在 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:298) 在 java.util.concurrent.ThreadPoolExecutor.runWorker(未知来源) 在 java.util .concurrent.ThreadPoolExecutor$Worker.run(来源不明) at java.lang.Thread.run(来源不明)


Gary,我感受到你的痛苦,因为我也经历过各种“不应该发生”的疯狂 CF 错误。我真的花了years我最近才了解到,与 Transfer ORM 中的间歇性故障作斗争是由于 xmlSearch() 不是线程安全的!

因此,让我分享一些策略,这些策略不会解决您的问题,但可以帮助您更接近诊断问题的根本原因:

使用duplicate()消除线程安全问题

上面的 XML 案例的解决方案是将 xmlSearch() 的结果包装在重复()中。这可能不是这里的问题,但您可以尝试将结构键的生成与用法分开,例如:

<cfloop query="Local.GetParts">
    <cfset local.id = "item_" & duplicate(Local.GetParts.ItemID) />
    <cfset Local.ReturnStruct[local.id] = {CurrentPrice: Local.GetParts['price' & Arguments.PriceBand]} />
</cfloop>

使用 cflog 进行健全性检查

这可能是我最好的策略,因为它告诉你 CF 即将呕吐时的状况。尝试类似的方法:

<cfloop query="Local.GetParts">
    <cfset Local.ReturnStruct['item_' & Local.GetParts.ItemID] = {} />
    <cftry>
        <cfset Local.ReturnStruct['item_' & Local.GetParts.itemID].CurrentPrice = Local.GetParts['price' & Arguments.PriceBand] />
         <cfcatch type="any">
             <cflog file="crazy.log" text="GetParts: #currentRow#/#local.getParts.recordCount#, KeyCount: #structKeyCount(local.ReturnStruct)#, keys: #structKeyList(local.ReturnStruct)#" />
         </cfcatch>
    </cftry>
</cfloop>

<cfreturn Local.ReturnStruct />

这个想法是,当您遇到意外错误时,记录您认为可能有助于您推断当前位置信息的所有内容。奖励:这实际上不会向用户抛出错误。

PS - 我注意到的一件事是,如果您的查询不返回结果,则 Local.ReturnStruct 将是未定义的。我个人会在函数的开头定义它,例如:<cfset local.returnStruct = {} />

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在同一请求中创建和更新结构时可能出现的竞争条件 - Coldfusion 的相关文章

  • 在 iOS 中,线程安全意味着什么?

    我经常遇到 线程安全 这个关键术语 并想知道它的含义 例如 在 Firebase 或 Realm 中 某些对象被视为 线程安全 线程安全到底意味着什么 线程不安全 是否允许多个线程同时修改任何对象 线程安全 如果任何对象不允许被多个线程同时
  • AtomicInteger 线程安全

    我怎样才能实现这里的 while 循环总是恰好执行 100 次 当我执行代码时 在极少数情况下 它会在控制台上打印 99 或 98 行 而不总是 100 行 这是我不明白的 import java util ArrayList import
  • C# 7 基于元组的变量交换线程安全吗?

    在 C 7 的元组之前 交换两个变量的标准方法如下 var foo 5 var bar 10 var temp foo foo bar bar temp 但现在我们可以使用 foo bar bar foo 它在一条线上 而且更漂亮 但它是线
  • C# 中的三元运算符 (?:) 线程安全吗?

    考虑以下两种获得较大数字的替代方案currentPrice and 100 int price currentPrice gt 100 currentPrice 100 int price Math Max currentPrice 100
  • 我是否需要一个并发集合来通过多个线程将元素添加到列表中?

    static final Collection
  • 如何使用 queryExecute 设置调试输出查询名称

    我想知道是否有办法使用 queryExecute 设置调试输出查询名称
  • 从返回的 Oracle 时间戳列检索数据

    我们有一个使用 Oracle 时间戳的 ColdFusion 8 Linux 应用程序 我们刚刚从 10g 转换为 Oracle 11g 现在在数据源上使用 Oracle 的瘦客户端 我们在选择时间戳列的应用程序中遇到错误 看起来好像是一个
  • ColdFusion UI 标签到 jQuery 的转换

    我正在尝试将一些具有各种 ColdFusion UI 标签的代码转换为 jQuery 我必须制定一个迁移计划 目前这就是我所拥有的 Tag Replacement CFApplet CFCalendar http jqueryui com
  • Coldfusion onCFCRequest 将 XML 的返回类型更改为 WDDX

    我的客户群终于不再使用 Coldfusion 8 所以现在我可以利用 Coldfusion 9Application cfc gt onCFCRequest事件 我有一个测试场景设置 但结果不是我所期望的 我有一个我调用的方法 它会产生一个
  • 登录后重定向和会话数据传输到另一个服务器/域

    经过一段时间的搜索和阅读文档后 我决定问你们 所以 场景很 简单 用户前往https Domain1 com https Domain1 com 输入他的凭据 尝试登录 登录成功后 根据用户类型和数据库中的其他信息 Domain1 服务器应
  • 我什么时候应该在 ColdFusion 中使用#?

    这是教授新人 ColdFusion 的最大障碍之一 何时使用 充其量是不明确的 由于使用它们通常不会产生问题 因此大多数人似乎倾向于过度使用它们 那么 基本规则是什么 我认为说哪里不使用 可能更容易 唯一的地方是在 cfif 语句和 cfs
  • ColdFusion 参数化查询

    我运行一个查询来填充我试图参数化的 CFChart
  • ASyncTasks 阻塞其他任务

    我有 2 个 ASyncTasks 一个从 httpPost 检索值 另一个更新 UI 的一些元素 包括列表视图 问题是 由于两个 ASyncTasks 共享相同的后台线程 如果网络操作首先启动并且运行缓慢 由于网络连接不良 其他后台线程花
  • 多线程 - 在数组中我应该保护什么?

    我正在编写一些具有全局数组的代码 该数组可以由两个线程访问以进行读写目的 读取或写入一系列索引时不会进行批处理 因此我试图弄清楚是否应该锁定整个数组或仅锁定我当前正在使用的数组索引 最简单的解决方案是将数组视为 CS 并在其周围放置一个大锁
  • 根据 rowversion 值更新记录?

    我最近实现了SQLrowversion以防止我的系统中出现并发问题 我用rowversion更新表中的单行时在 where 子句中 到目前为止 我已经测试过 似乎是一个很好的解决方案 现在我正在寻找一种简单的方法来在我的系统中实现此功能 这
  • 如何在 CFscript 中指定参数属性? (CF9)

    在 CF9 文档中 在 CFScript 中定义组件和函数 http help adobe com en US ColdFusion 9 0 Developing WSE99A664D 44E3 44d1 92A0 5FDF8D82B55C
  • Stopwatch.ElapsedTicks 线程安全吗?

    如果我有一个共享的System Diagnostics Stopwatch实例 可以多线程调用shared ElapsedTicks以安全的方式并获得准确的结果 以这种方式使用 Stopwatch 的共享实例与使用静态GetTimeStam
  • NHibernate 会话线程安全

    我已经使用 NHibernate 一段时间了 并且不时发现如果我尝试同时请求两个页面 或尽可能接近 偶尔会出错 所以我认为这是因为我的会话管理不是线程安全的 我以为这是我的课 所以我尝试使用与这篇博客文章不同的方法http pwigle w
  • 如何在链接到表单的工作表中执行 Google 工作表脚本之前等待 Google 表单脚本完成执行

    我有两个脚本 一个链接到 Google 表单 另一个链接到 Google 表格 我需要确保 Google 表单脚本在执行链接到 Google 表单的工作表的 Google 工作表脚本主体之前完成执行 如何才能做到这一点 需要等待 Googl
  • 单线程公寓问题

    从我的主窗体中 我调用以下命令来打开一个新窗体 MyForm sth new MyForm sth show 一切都很好 但是这个表单有一个组合框 当我将其 AutoCompleteMode 切换为建议和追加时 我在显示表单时遇到了这个异常

随机推荐

  • Rails 每个循环每 6 个项目插入标签?

    我有 X 个图像对象 需要在视图中循环遍历 并希望每 6 个对象左右创建一个新的 div 对于画廊 我看过cycle 但它似乎改变了所有其他记录 有谁知道每 6 次向视图中插入代码的方法吗 我可能可以用嵌套循环来做到这一点 但我对这个有点难
  • C++ 输入运算符重载

    我正在尝试重载我创建的 UserLogin 类上的输入运算符 不会引发编译时错误 但也不会设置值 一切都在运行 但 ul 的内容仍然存在 字符串 id 是 sally 登录时间为00 00 注销时间为 00 00 入口点 include
  • 如何使 videojs 标记可滑动或可移动

    我想移动我的markers每当它随着搜索一起滑动时 我希望我的标记准确无误slidable as jqueryui 滑块 问题 我想要我的markers 两者 一样可滑动jqueryui range滑块如以下示例中的视频所示 var pla
  • Retrofit+OkHttp 发送 GET 请求时可以,但发送 POST 时给出 SocketTimetout

    我从 Retrofit 开始 可以成功执行 GET 请求 但是当我尝试执行 POST 或 PUT 请求时 出现 SocketTimeOut 异常 我根据以下内容将 OkHttp 添加到我的 libs 文件夹中这个问题 https stack
  • @JsonInclude(Ininclude.NON_NULL) 未按预期工作

    我已经添加了 JsonInclude Include NON NULL Response 类上的注释 JsonInclude Include NON NULL public class Response JsonProperty priva
  • Nuxt - 将脚本添加到头部和主体

    我正在尝试在我的 Nuxt 应用程序中使用此脚本 但不知道如何操作 在基本的 HTML 文件中 它工作得很好 这是代码
  • Hibernate Envers 修订信息(更改列表)

    我想在我的项目中添加修订更改列表 单击信息图标 例如 Revision X added fieldA entry modified fieladB from B to BB removed fieldC entry 哪个是最好的方法 ps
  • Xcode 连接到 MS SQL 数据库

    我有一个现有数据库已在远程启动并运行MS SQL server 并且我希望能够与该数据库进行通信和交互Xcode 我正在写一份申请OS X in Swift以及应用程序应使用的数据存储在该远程数据库中 问题是我好像找不到Swift可以连接到
  • 如何传递 bquote 的符号字符串以在 ggplot 中求值?

    我在函数中创建的 ggplot 的轴标签有所不同 有些标签有上标 下标 而另一些则没有 例子 m data lt data frame x runif 10 y runif 10 x labs lt c rain mm light W m
  • array_walk 匿名函数

    有没有办法让我用匿名函数来获取这个数组来设置值 url array dog cat fish array walk url function value key url key str replace dog value echo pre
  • Azure CLI 运行命令使用参数调用 RunPowerShellScript

    我一直在尝试在 Azure VM 上运行一个脚本 该脚本需要像这样传递参数 az vm run command invoke g
  • scikit learn:与 GridSearchCV 兼容的自定义分类器

    我已经实现了自己的分类器 现在我想对其运行网格搜索 但出现以下错误 estimator fit X train y train fit params TypeError fit takes 2 positional arguments bu
  • ASP>net MVC 可重用部分

    在 winforms 和 ASP net 中使用 net 几年后 我现在开始进入 MVC 我知道有点晚了 对我来说 一个主要的困惑是可重用 组件 的概念 类似于网络表单中用户控件的概念 例如 我希望在我的网站的会员区域内有许多 小部件 其中
  • OpenCL 内置函数“选择”

    我不清楚内置 OpenCL 函数的目的是什么select 有人可以澄清一下吗 来自 OpenCL 规范 功能选择 基因型a 基因型b 基因型c 返回 对于向量类型的每个分量 结果 i 如果设置了 c i 的 MSB b i a i 在这种情
  • 在 asp.net C# 中使用客户端 ID 和客户端密钥访问 Sharepoint 列表

    目前 我可以使用用户 ID 和密码访问共享点列表 如下所示 但想了解如何使用客户端 ID 和客户端密码访问列表 string siteUrl https xyz sharepoint com sites MyList ClientConte
  • VBA 运行时错误 3134

    以下代码创建一个 SQL 字符串 该字符串在 MS Access 中产生语法错误 3134 sql INSERT INTO tblItems desc descExtended itemNumber currentPrice VALUES
  • Spring Security 加密 MD5

    我有一个使用 spring 框架和 spring security 进行登录的 java web 应用程序 在我的数据库中 我的密码在保存之前已加密为 MD5 我在 application config xml 中添加了这段代码
  • jQuery DataTable - 搜索一列下拉列表

    我有一个简单的 jQuery 数据表 其中包含 4 列 其中一列是下拉列表 table tfoot tr th class searchBox Vendor Location th th class searchBox Currency t
  • RestKit valueTransformer 没有被调用

    我正在使用 RestKit 与我的 JSON 端点对话 端点返回一个以 毫秒数 为单位的 UNIX 时间戳 但是 RestKit 的默认转换器假定它是 秒数 并且我在 NSDate 中得到了错误的值 所以我环顾四周 发现我需要使用自定义 v
  • 在同一请求中创建和更新结构时可能出现的竞争条件 - Coldfusion

    大约一年前 我问了一个关于我在应用程序中遇到的错误的问题 该错误表明可能存在竞争条件 在 ColdFusion 中创建结构体时可能存在竞争条件 https stackoverflow com questions 19859690 possi