低代码到底是不是行业毒瘤?一线大厂怎么做的?戳此了解>>> 了解详情
写点什么

混淆了的工作单元和线程

2007 年 9 月 17 日

许多服务器端的应用和许多桌面应用都含有与一个特殊任务的执行有关的数据。常见的解决方案是将这类数据放到线程本地存储区中,将变量中的数据与其执行线程相绑定。的确很方便,但这是一个基于有缺陷的假设的实践方案。

Bob Martin 写了一篇关于假设线程跟工作单元存在一对一关系的专题文章:

ThreadLocal 变量对一个给定的线程而言是一个极为便利的关联数据的方式。例如,类似于 Hibernate 这样的框架利用它来保存会话信息。但是,这种 practice 非常依赖于将一个线程和一个工作单元等同视之。这是一种错误的假设。

ThreadLocal 是一个 Java 术语,但其构造在多线程环境中是通用的。Bob 还记得:

十三年前写我的第一本书时,我和 Jim Coplien 针对线程和对象的本质发生了争论。他做出了一个令我如今仍记忆犹新的澄清,他说:“一个对象是功能的抽象,一个线程是调度的抽象”。

当前,将工作单元中的数据映射到一个线程中是一个标准的模式,一个在许多流行的框架中可以发现的模式,但即便是这种方法存在了相当长的时间,这个不完美的抽象仍然缺乏一些情景。

在一个任务的不同部分存在不同的优先级是很常见的事情,而且也没有规则约束说一个任务必须是单线程的。Bob 举例说明一个工作单元在基于得到的数据执行一个相当长时间的计算时,可能会非常需要跟一个外部服务的响应性沟通,这样一个问题通常是分解成两个线程来解决。他问道:

工作单元相关的变量应该放在哪儿?它们不应被保存到 ThreadLocal 中,因为任务的每一个部分在一个分离的线程中运行. 它们也不能被保存在静态变量中,因为有很多的线程。最终答案是它们必须在线程间作为栈中的功能参数进行传递,并且被记录在存放于 queue 队列之中的数据结构内。

TapsaKoo 不久前也遇到了一个同样的情况。当在 WinForms 中努力尝试领域驱动的方式时,他描述了他寻找一个保存会话专有数据的问题

如果应用程序每次只有一个表单被打开,我会把会话对象保存到 Callcontext。如果应用每次打开多个表单,并且这些表单希望拥有我的 session 类的一个单独的实例时该怎么办?CallContext 已经不能满足要求了。那么全都是线程专有的备选方案吗?还剩下什么了?什么也没有剩下?我已经不是考虑这个问题的第一个人了。可能会有存在一个解决方案,但我找不到它。我应该把会话对象注入达到需要它的每个对象实例中吗?还是我将领域类的许多行为重构到服务中,然后将会话对象注入其中?我不太喜欢这种方式,因为我希望我的类被数据容器有更多的含义。

在阅读完 Bob 大叔的回复后,TapsaKoo也同意对此问题没有轻松的解决方案

无论你有 1 个还是 10 个线程,问题总是相同的。工作单元或者会话状态应该存放在一个不依赖于线程的地方。认为工作单元直接对应于一个单个的线程是非常危险的假设。这种假设会严重限制你的其他的架构性选择。

Bob 推断可能丢失了某些东西:

所以,尽管是司空见惯,ThreadLocal 变量混淆了调度与功能的分离问题。它们诱惑我们将功能和调度耦合在了一起。这是非常不幸的事情,因为功能和调度的对应是脆弱的、偶然的。我们实际上应该做的是让建立一个 UnitOfWorkLocal 变量成为可能。

查看英文原文: Confusing unit-of-work with threads - - - - - -

译者简介:孙向晖,儿子小名“豆豆”,常被人称为“豆豆他爹”。1998 年开始步入 IT 行业,现任浪潮软件质保中心副主任。专注于研究和实践 MDA/UP/UML/SCM 等相关技术在团队中的大规模应用,对产品化的软件项目管理、需求管理和配置管理略有心得。他的博客为 http://blog.csdn.net/xiaosun/ 。参与 InfoQ 中文站内容建设,请邮件至 china-editorial[at]infoq.com

2007 年 9 月 17 日 22:00392

评论

发布
暂无评论
发现更多内容

游戏夜读 | 如何分析游戏体验?

game1night

不会有人还不知道全文检索工具Lucene怎么用吧?文字长文教程

给你买橘子

Java 搜索引擎 lucene 程序员 开发工具

架构师训练营第六周总结

陈靓-哲露

啃碎并发(九):内存模型之基础概述

猿灯塔

Java 猿灯塔

图说前端-内存管理(1/3)

梦见君笑

前端 内存 前端进阶训练营

分布式系统的一些基础理论

俊俊哥

分布式事务 CAP Base

redis里的数据结构

流沙

redis

数据分析之AB testing实战(附Python代码)

JackTian

Python 编程 程序员 数据分析 AB testing实战

ARTS 打卡 第2周

Scotty

那些让程序员目瞪口呆的Bug

Java小咖秀

程序员 程序员人生 bug

架构师必须知道的架构知识

Chank

架构 架构师 Architecture Architect

基础篇:JAVA基本类型

csc

Java Java 25 周年

DolphinScheduler-1.3.0-dev功能体验

Eights

hadoop 大数据任务调度

如何基于 BitMap 进行海量数据分析

GrowingIO技术专栏

互联网 数据分析 科技互联网 数据化

给 Spring Boot 项目减减肥!18.18M 到 0.18M 是如何做到的?

给你买橘子

Java 程序员 Spring Cloud 编码 SpringBoot 2

图说前端-ArrayBuffers 和 SharedArrayBuffers(2/3)

梦见君笑

前端 内存管理 前端进阶训练营

Java 线程的生老病死

武培轩

Java 线程 多线程 并发 线程状态

如何搭建一个HBase集群

Rayjun

HBase

图说前端-使用Atomics避免SharedArrayBuffers中的race conditions(3/3)

梦见君笑

前端 内存管理 前端进阶训练营

RESTful 架构及实践

pingan8787

Java 前端 RESTf

架构师训练营第六周作业

张明森

架构师训练营第六周作业

hiqian

DOM 树的构建

法正

html DOM 前端进阶训练营

基于Kubernetes实现的大数据采集与存储实践总结

岿然独存5

Docker Kubernetes S3 EFK Fluentd

计算机的时钟(一):NTP协议

ElvinYang

【计算机网络】网络层——路由器与路由选择协议

烫烫烫个喵啊

计算机网络 网络层

架构师训练营第六周总结

hiqian

redis系列之——Redis为什么这么快?

诸葛小猿

Java redis 程序员

java 后端博客系统文章系统——No3

猿灯塔

基础篇:Object对象

csc

Java Java 25 周年

云原生实践系列:概述

孤岛旭日

Serverless 微服务 Service Mesh 服务架构

2021 ThoughtWorks 技术雷达峰会

2021 ThoughtWorks 技术雷达峰会

混淆了的工作单元和线程-InfoQ