IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

标签:Concurrency

共 40 篇相关文章

IT 累计浏览 1,989

让 lua 运行时动态切换操作系统线程

这篇讲的是开发者在构建跨平台游戏引擎时,如何巧妙解决一个操作系统级的线程调度矛盾。作者从 iOS 的一个严苛限制出发:系统要求窗口消息循环必须运行在主线程,否则程序可能被杀;而引擎为了隔离耗时的业务逻辑,又必须把窗口管理模块与用户主逻辑分到不同线程。 矛盾在于,用户的业务代码期望运行在 Lua 解释器启动时的主虚拟机(VM)中,窗口模块期望在独立线程,同时窗口模块还必须占据操作系统意义上的“主线程”。作者最初认为这无解,除非像 Skynet 那样深度定制 Lua 运行时,让 VM 能自由迁移。 真正的转机来自一个巧妙的 API 设计:`thread.fork`。它通常让 func1 在当前 VM,func2 在新建 VM 和线程上并行。但作者反其道而行,让 func1(用户主逻辑)在**新线程**上运行,而让 func2(窗口模块的新 VM)继续留在**当前线程**(即操作系统主线程)上执行。由于两者都通过 `pcall` 被限制在各自作用域内,用户代码完全感知不到自身线程已切换,而窗口模块则恰好满足了系统对主线程的要求。 这个方案的巧妙之处在于,没有去硬撼操作系统的规则,而是通过“偷梁换柱”——交换两个执行流所在线程的位置,让看似不可调和的约束在架构层得到了圆满解决。

IT 累计浏览 2,607

协程并发模型及使用感受

这篇讲的是协程并发模型在真实项目中的“两面性”。作者以一个Python项目为例,分享了使用gevent协程后的编程体验:它让并发模型变得简洁,一个协程对应一个任务,抛弃了传统的线程池。但文章重点剖析了在CPU资源受限的单核环境下,协程暴露的一系列生产陷阱。 文中指出的陷阱非常具体:协程中的间接死循环会导致其他协程被饿死;引入了未被“green化”的阻塞库(如MySQL-python)会阻塞整个事件循环,导致调度延迟;在单核CPU被压榨到80%-90%时,无法设定优先级的协程库会使高时延敏感的API协程与耗时任务协程争夺资源,影响服务质量。此外,程序员在自动切换环境下容易忽略协程挂起(如长时间sleep)对整个协程池吞吐量的影响。 作者最终的实践是,为了规避CPU瓶颈,项目还是演进到了多进程结构。文章总结道,协程在低CPU系统中能带来编程简便性,但当系统负载上去后,其资源管理和调试的复杂度可能会抵消甚至超过多线程模型。对于考虑引入协程的开发者而言,这篇经验分享提前点明了从理论便利走向工程现实时需要应对的挑战。

IT 累计浏览 2,169

[译]Go开发中一些有用的模式

作者从使用VB、Java、C#和Python转向Go开发的视角出发,分享了在Go中实现几个经典设计模式的独特方式。文章的核心在于对比:与许多语言依赖注解(Annotation)实现装饰器不同,Go通过函数包装和接口适配来增强功能,使控制流更显式,避免了隐藏的配置陷阱。对于单例模式,Go利用`sync.Once`优雅地解决了其他语言中常见的并发初始化安全与性能问题,甚至结合装饰器模式将不安全的API包装成线程安全版本。此外,文章还介绍了用类型方法实现“静态成员”的技巧,以及如何用带缓冲的channel轻量级模拟信号量。这些示例不仅展示了Go的语法特性,更体现了其通过组合和并发原语来构建清晰、安全代码的哲学,对习惯其他语言范式的开发者很有启发。

IT 累计浏览 2,155

【死磕Java并发】—–Java内存模型之从JMM角度分析DCL

这篇讲的是Java并发编程中一个经典而隐蔽的陷阱——双重检查锁定(DCL)在实现单例模式时,看似高效完美,实则潜藏因指令重排序导致的对象未初始化即被引用的严重问题。 作者从一个常见的错误懒汉式单例写法入手,逐步推导到试图优化性能的DCL写法。其核心矛盾在于:对象实例化包含“分配内存”、“初始化对象”和“引用赋值”三个步骤,而JVM或CPU可能对后两步进行重排序。这就意味着,在多线程环境下,其他线程可能看到一个已完成引用赋值、但尚未完成初始化的对象“半成品”,导致程序行为不可预期。 文章进而给出了两种标准解决方案。一种是利用`volatile`关键字的内存屏障语义,禁止实例初始化步骤的重排序,从而确保对象完全构造完毕后对其他线程才可见。另一种则是利用类加载机制,通过静态内部类延迟初始化,借助JVM对类初始化过程加锁的保证,来实现线程安全的单例。后者巧妙地将同步问题委托给了JVM本身,代码也更为简洁。 理解DCL的问题根源及解决方案,对于掌握Java内存模型中多线程可见性与指令重排序的实际影响至关重要。

IT 累计浏览 2,348

【死磕Java并发】—–深入分析synchronized的实现原理

这篇讲的是Java开发者最熟悉的synchronized关键字,但深入到了它鲜为人知的底层实现。文章从开发者早期对synchronized“重量级锁”的刻板印象出发,系统剖析了JDK 1.6对其进行的一系列优化,彻底改变了这一认知。 作者首先拆解了同步代码块与同步方法在JVM字节码层面的不同实现(monitorenter/monitorexit指令与ACC_SYNCHRONIZED标志),并指出这一切的基础在于Java对象头中的Mark Word和Monitor结构。其中,Mark Word被设计成可随锁状态动态复用的高效数据结构,是理解锁升级的关键。 文章的核心价值在于清晰梳理了JVM的锁优化机制:从通过“自旋等待”避免线程频繁挂起,到引入“适应性自旋”让虚拟机智能调整等待次数;从利用逃逸分析“消除”不必要的锁,到将连续操作“粗化”为更大范围的锁。最终,重点阐释了锁状态如何从无锁、偏向锁、轻量级锁一步步升级到重量级锁的完整路径,以及每一步升级的触发条件与设计意图。这不仅是理解synchronized的百科全书,也为开发者在实际编程中合理使用同步工具提供了底层视角。

IT 累计浏览 1,071

专注和游离

作者在文中探讨了一个普遍困扰:为何总渴望“21天精通”,却常在该专注时沉迷碎片信息,在该放松时又陷入焦虑。他直指那种“快速提升”的幻想往往是骗局,真正的成长是持续、渐进的“日拱一卒”。 但作者也承认,在诱惑泛滥的时代,坚持“日拱一卒”极难。他分享了自己摸索出的方法:将晚上时间刻意切分为“专注”与“游离”两块。专注时段屏蔽一切干扰,全力用于写作、编程或深度学习;游离时段则坦然刷圈、看剧、泛读,用于放松和拓宽视野。这种有节奏的交替,让他能在效率和广度上逐步积累优势。 文章最终落脚于一个朴素的信念:对于大多数普通人,接纳成长的缓慢节奏,找到适合自己的专注与游离的平衡点,坚持下去,就是通往“大器晚成”的路径。文末引用的“十年学会程序设计”七条建议,如重实践、多交流、学多门语言,也为这条长期主义的道路提供了具体的路标。

IT 累计浏览 3,002

Java处理InterruptedException异常小结

这篇文章聚焦于Java开发中一个常见却容易被误用的异常处理场景:如何正确对待 `InterruptedException`。作者指出,许多开发者习惯性地“吞掉”这个异常,但这会损害程序响应中断、实现优雅取消的能力。 文章的核心在于阐明 `InterruptedException` 的特殊含义:当一个方法抛出此异常,它不仅是一个检查型异常,更是在声明自己是一个可被中断的阻塞方法。基于此,作者对比了几种关键的处理策略:其一是将异常直接向上抛出;其二是在进行必要的清理工作后重新抛出;其三,当在Runnable中无法抛出时,必须通过 `Thread.currentThread().interrupt()` 恢复中断状态。文中同时明确批判了仅仅记录日志或完全忽略的“生吞”做法。 通过代码示例,文章清晰地展示了正确与错误模式之间的分野,并解释了为何必须保留中断信号——因为中断是协作式的,且可能由线程池等机制多重消费。最终,作者建议通过轮询中断状态来实现灵活的、可取消的任务。整篇文章为处理这类并发问题提供了明确、实用的操作指南。

IT 累计浏览 3,913

Linux下互斥量加锁与解锁操作的C代码实现

这篇讲的是在多线程编程中如何安全地保护共享资源。作者从一个常见问题出发:当多个线程需要修改同一全局变量时,必须确保操作的原子性,否则容易引发不可预知的错误。 文章核心是演示一套完整的互斥量加锁与解锁C语言实现。作者没有停留在理论,而是直接给出了可运行的代码文件“LockAndUnlock.c”。其中,自定义的`MutexLock`函数封装了`pthread_mutex_timedlock`,并通过`gettimeofday`获取系统时间,巧妙地计算出带超时(5秒)的绝对等待时间,避免了线程可能被永久阻塞的风险。`MutexUnLock`函数则简洁地封装了解锁操作。 代码结构清晰,包含了宏定义、函数声明和完整的错误处理逻辑。文章最后还附上了在Linux下的具体编译命令(gcc -pthread)和运行结果,形成了一个从问题、方案到验证的闭环。对于需要在C程序中使用POSIX线程互斥机制的开发者来说,这套封装好的函数可以直接作为参考或API使用。

IT 累计浏览 5,311

进程和线程关系及区别

这篇讲的是操作系统中进程与线程的基础概念辨析。作者从定义出发,开篇就给出了明确定义:进程是资源分配和调度的独立单位,线程则是CPU调度的基本单位,且线程几乎不拥有系统资源,仅保留运行必需的最小集。 文章的核心在于对比二者的根本差异。关键区别在于资源管理方式:进程拥有独立的地址空间,因此一个进程崩溃不会波及其他,健壮性更高但切换开销大;而同一进程内的线程共享全部内存资源,这提升了并发效率与数据共享的便利性,但一个线程的异常往往会导致整个进程终止。 这种设计上的权衡,直接导向了不同的适用场景。对于需要高并发、共享数据且追求执行效率的任务,多线程是更优的选择。反之,对于需要更强隔离性、运行于多台机器或更看重稳定性的场景,多进程模型则更为可靠。文章最后也点明了线程执行开销小但不利于资源保护,进程则相反的特点。

IT 累计浏览 7,245

程序中的“多线程”

这篇讲的是程序设计中“多线程”的基础概念,作者从大家熟悉的“工厂流水线”比喻切入,清晰地区分了“单线程”与“多线程”的工作方式。单线程像严格的流水线,必须前一步完成后才能做下一步;而多线程则允许不同任务并行执行,互不阻塞。 文章用生成、上传、删除话单文件的实例,对比了两种模式的流程图。单线程是顺序的三步走,多线程则是三个线程同时运行,各自负责独立的功能。这种对比直观展示了多线程如何实现真正的并行处理。 作者接着总结了多线程在大型软件中的优势:让程序逻辑更清晰、易于阅读;通过并行执行提升效率;同时增强模块化,便于问题追踪。这些好处都源于其将大任务拆解为可独立运行的小流程的设计思想。 总的来说,这篇文章用通俗的比喻和具体的代码场景,阐明了多线程作为一种核心编程方法的价值——它不仅是技术实现,更是一种让软件变得更高效、更健壮的设计哲学。

IT 累计浏览 14,212

无锁消息队列

这篇讲的是如何在共享内存中设计高效的无锁消息队列。作者从实际项目需求出发——为了将耗时的数据落地任务从主逻辑进程中剥离,以提升整体处理能力——提出了用无锁队列替代频繁系统调用的方案。 文章的核心是从简单到复杂,逐步推演无锁队列的设计。首先探讨了最基础的单生产者与单消费者场景,仅需维护 front 和 rear 指针,利用循环队列即可高效工作。接着,为解决多消费者并发出队的问题,引入了 CAS(Compare & Set)原子操作来安全地更新指针。最后,在多生产者多消费者的最复杂场景下,通过增加一个 write_index 变量,结合两次 CAS 操作来协调生产者之间的写入竞争,确保了数据一致性。 文章结合具体图示和伪代码,清晰地阐述了不同并发模型下的实现关键与细微差别,例如利用 CAS 实现“乐观锁”,以及在生产者操作失败时通过 sched_yield() 让出 CPU 的优化技巧。作者在项目中实际应用了其中一种设计,最终观察到 CPU 使用率下降了约10%,验证了该方案的有效性。

IT 累计浏览 4,113

一个“蝇量级” C 语言协程库

这篇讲的是如何用不到100行代码在C语言里实现协程。作者从协程的背景讲起,对比了它和线程在“生产者-消费者”模型中的差异,指出C语言由于依赖栈帧实现函数调用,缺乏原生yield语义,直接实现对称协程比较麻烦。 文章核心介绍了开源库protothreads。这个库“蝇量级”到所有代码都在5个头文件里,每个协程只需2字节内存开销,非常适合资源敏感的嵌入式场景。作者还追溯了作者Adam Dunkels的其他知名作品,暗示其工业级可靠性。 实现的关键在于一个C语言“奇技淫巧”:利用switch-case语句的穿透特性和`__LINE__`宏,模拟了程序计数器的保存与恢复。通过将return语句嵌入case标签,并在函数入口用一个switch跳转到上次的挂起点,就用纯C语法实现了协程的挂起与恢复。文章最后展示了如何用宏封装出Begin/Yield/End这样的简洁接口,让开发者能写出类似Python生成器的代码结构。

IT 累计浏览 3,290

lock free的理解

这篇文章帮读者厘清了一个常见误解:很多人以为“无锁”(lock free)就是指程序不使用互斥锁,但实际上这个概念与“用不用锁”并无直接关系。作者指出,lock free的正确定义是:程序能够保证在所有线程中,至少有一个线程可以持续推进,而不会互相阻塞。这意味着即使某个线程挂掉,整个程序的执行流依然能够向前。 文章用一个典型的无锁循环代码举例——两个线程不断交替修改同一个变量,结果却可能互相卡死,这恰恰说明“不用锁”未必就是 lock free。相反,使用锁也可能实现 lock free 的特性,关键在于设计是否保证了系统整体的进展性。 最后,作者提到在实际编码中,lock free 的实现通常依赖 CAS(Compare and Swap)这类硬件支持的原子操作,从而在避免传统锁开销的同时,保障并发安全与性能。

IT 累计浏览 3,952

Web 开发程序员谈网游服务器开发

这篇讲的是作者在参加一次网游开发团队交流后的思考。他敏锐地捕捉到,传统网游服务器开发因逻辑与存储高度绑定,往往忽视了动态扩展与容灾能力,而这些恰恰是Web开发领域的强项。 作者的核心观点是,网游服务器可以借鉴Web架构的“服务无状态化”原则。关键在于将服务拆分为“逻辑(指令)”和“存储(状态)”两部分。一旦逻辑层本身无状态,就能像典型的Web应用(如PHP + MySQL)一样,实现服务器的弹性增减。即使面对“用户切换服务器后状态丢失”这类网游特有疑问,通过分离设计和将存储层集群化,同样能构建出高可扩展、高容灾的系统。 这个视角为游戏后台架构提供了一条清晰的优化路径:用Web成熟的工程思想,去解耦游戏服务器的紧耦合状态。

IT 累计浏览 23,534

一种常见的并发编程场景的处理

这篇讲的是并发编程中一个容易被忽略的典型场景:当一个“守护线程”提供共享资源,多个业务线程频繁读写,而主线程仅偶尔需要介入(比如统计数据、做快照)时,如何设计才能兼顾性能与数据安全? 文章指出,如果主线程和业务线程使用同一把锁(如 `synchronized`),在绝大多数(99.9%)主线程不介入的时间里,业务线程之间会产生不必要的锁竞争,拖累性能。作者给出的方案是引入 `AtomicBoolean` 和 `AtomicInteger` 两个原子变量(volatile 变量的实现)来协调状态:一个标志主线程是否正在独占操作,另一个统计当前正在写入的业务线程数。这样,业务线程只需在主线程介入时等待,而彼此之间几乎无需竞争锁,从而在大多数时间里实现近乎无锁的并发写入。 文章以 `ConcurrentHashMap` 为例给出了核心代码,并坦承在 Java 中这点性能差异或许可以忽略,且方案本身有一定复杂度。但作者认为,这种在“性能和数据保护之间寻求最大平衡”的设计思路,其实践意义是利大于弊的。

IT 累计浏览 5,354

7个示例科普CPU Cache

这篇文章从一个有趣的角度切入:用7个直观的C#代码实验,揭示了CPU缓存(Cache)如何深刻影响程序性能。作者并非空谈理论,而是带着读者一步步“看到”硬件的工作方式。 文章开篇就通过两个循环运行时间几乎相同的“反直觉”案例,点明了关键:程序的瓶颈往往在内存访问而非计算本身。随后,通过调整循环步长的实验,清晰地展示了“缓存行”(Cache Line)的概念——CPU以64字节块为单位读写内存,这直接解释了为何步长在16以内时性能恒定。 实验进一步深入。通过改变数组大小,文章用性能图表直观呈现了L1、L2缓存的容量阈值,程序运行时间在数据超出缓存大小时急剧变慢。接着,两个对比循环揭示了“指令级并发”的奥秘:操作间的依赖关系决定了CPU能否并行执行指令。 文章最后探讨了更为进阶的“缓存关联性”问题,解释了直接映射、N路组关联等设计如何在效率和冲突之间取得平衡,并说明了物理地址如何决定缓存槽的竞争关系。 总体来说,这篇译文将抽象的计算机体系结构知识,转化为了可运行、可观察的代码案例与性能图表。它没有停留在“缓存很快”的表面结论,而是带你探查缓存行、容量、关联性这些具体机制如何在代码层面产生实际影响,对于理解性能优化非常有启发。

IT 累计浏览 2,829

Erlang集群RPC通道拥塞问题及解决方案

当Erlang集群采用默认的全联通架构时,节点间通过RPC通道的密集调用可能引发严重的通道拥塞。文章从社区主流的分层服务架构出发,指出大量节点间消息流易导致dist端口忙,进而阻塞发送进程——由于RPC模块基于单进程gen_server,这种阻塞会直接拖累系统响应时间。 作者指出,这类问题可通过`erlang:system_monitor`的`busy_dist_port`事件及时感知,例如Riak系统便利用此机制告警。解决关键在于调整`dist_buf_busy_limit`参数:默认1MB的分布缓冲区上限可能不足,通过启动参数`+zdbbl`将其调大(如Riak案例中增至8MB),即可显著缓解阻塞、提升吞吐。文章结合监控实践与参数调优案例,提供了从问题定位到彻底解决的完整路径。

IT 累计浏览 6,700

并发框架Disruptor译文

这篇讲的是Martin Fowler撰文推荐的高性能并发框架Disruptor,它正是LMAX交易系统能实现每秒600万订单的核心引擎。作者从“为什么会这么快”切入,剖析了传统锁机制的缺点,然后详细拆解了Disruptor的几大“魔法”:通过精心的缓存行填充避免伪共享、利用内存屏障保证无锁操作的正确性,并深入讲解了RingBuffer这个核心数据结构如何实现高效的读写。 文章不仅解释了原理,还提供了具体的使用指南,涵盖了从RingBuffer读取、写入到版本演进的完整路径。最后,通过LMAX架构和实际处理百万TPS的案例,展示了它在解决高并发、低延迟场景下的巨大价值。对于想理解无锁编程和设计高性能内存队列的开发者,这组系统性的译文提供了从理论到实践的清晰线索。

IT 累计浏览 4,711

进程上下文切换 – 残酷的性能杀手(下)

这篇讲的是进程上下文切换如何成为性能杀手的实测篇。作者从自己开源的并发网络库chaos中选取task_service模块,通过编译宏控制,对比了pthread条件变量、sleep、pipe、socketpair、eventfd以及boost::io_service这几种线程间通信机制的实际表现。 测试数据清晰展示了不同机制的CS/s(每秒上下文切换次数)和整体耗时:pthread条件变量切换高达60万次且最慢,而eventfd的切换次数低至200次,效率遥遥领先。有趣的是,boost::io_service的CS也高达75万次,但整体效率却比pthread模型更好,作者推测这与其内部高效的队列实现有关。 结论很直接:上下文切换次数与程序运行效率基本呈反比,减少CS是优化后台服务性能时必须考量的关键因素。文章用硬核的实测数据说话,为开发者选择并发模型提供了切实参考。

IT 累计浏览 5,199

Tips of Linux C programming

这篇文章分享了Linux内核和GNU C中一些不那么为人所知却非常实用的编程技巧。作者从链表的非常规定义讲起,展示了如何将链表节点嵌入到数据结构中,并利用`container_of`宏从节点地址反推出宿主结构体,这种方法比传统教科书定义更灵活优雅。 随后,文章深入到编译器与硬件层面:介绍了用`likely`/`unlikely`宏提示编译器优化分支预测,减少流水线冲刷;演示了通过内联汇编和`lock`指令前缀实现原子加法,保证多处理器环境下的数据一致性;还探讨了GNU C特有的零长度数组特性,用于在运行时动态分配结构体尾部的变长数组。最后,简短提到了三目运算符`a = x ? : y`这种简洁的省略写法。 这些技巧都源自真实的内核开发或GCC特性,能帮助C程序员写出更高效、更地道的代码。文章穿插了关键的代码片段和原理剖析,对希望提升底层编程技巧的读者很有启发。