IT技术博客大学习 共学习 共进步

标签:Multithreading

共 30 篇相关文章

IT 累计浏览 5

SetWindowText 引起的死锁

在基于ltask多线程框架的游戏开发中,使用sokol_app封装窗口时遇到了启动阶段偶发黑屏的问题。经排查,死锁源于跨线程调用SetWindowTextW修改窗口标题。由于该API内部通过SendMessageW投递WM_SETTEXT消息,要求窗口线程处理完毕才能返回,而窗口线程当时正阻塞在另一个同步锁上,形成循环等待。 Windows消息机制要求通过SendMessageW发送涉及字符串生命周期管理的系统消息,因此不能简单改用PostWindowTextW避免阻塞。最终解决方案是在ltask框架内创建一个独立服务来执行SetWindowTextW,通过服务间异步消息传递修改标题指令,从而避免阻塞主帧处理流程。这一案例揭示了多线程环境下GUI消息循环与任务调度器交互时的典型死锁陷阱,关键在于理解Windows线程消息队列的同步特性,并设计解耦的线程通信机制。

IT 累计浏览 148

嵌入主线程消息循环的任务调度器

这篇文章聚焦于将基于多线程调度器的项目 `soluna`,移植到 WebAssembly 等非 Windows 平台时遇到的核心难题。作者从项目维护的实际需求出发,解释了为何在可能退化为单线程的环境中,模拟出 `ltask` 调度器的能力成为关键挑战。 文中介绍的核心实现思路颇具巧思:将原本独立的任务调度器巧妙地“嵌入”到主线程自身的消息循环里。这意味着主线程需要同时承担两份工作——既要处理自己的消息队列,又要扮演调度器的角色,为其他逻辑“线程”(通常是协程)分配执行机会。作者没有停留在理论层面,而是深入探讨了如何在单一线程上,通过协作式调度和精心设计的队列,来模拟多线程环境下的任务切换与调度行为。 这种设计确保了项目的多线程调度架构得以保留,其核心价值不因平台变化而丧失。文章清晰地展示了一个现实场景下的工程权衡与技术适配过程,对于需要处理类似跨平台移植问题,或是对单线程高并发模型感兴趣的开发者而言,提供了非常具体的实现参考。

IT 累计浏览 2,587

协程并发模型及使用感受

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

IT 累计浏览 1,062

专注和游离

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

IT 累计浏览 2,980

Java处理InterruptedException异常小结

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

IT 累计浏览 7,220

程序中的“多线程”

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

IT 累计浏览 4,003

多线程下的fork及写时复制导致的性能问题

这篇讲的是贴吧在将服务从PHP-FPM迁移到HHVM(多线程模型)后,遭遇CPU使用率异常飙升的故障排查过程。问题的根源在于,程序中某个基础库调用exec执行shell命令时,会先fork进程。由于HHVM是多线程架构,其他线程在fork期间的内存写入,会频繁触发Linux内核的“写时复制”机制,导致大量不必要的内存拷贝,从而耗尽CPU资源。 作者详细剖析了写时复制的工作原理,指出在单进程模型(如PHP-FPM)下,fork后立刻exec的场景几乎不会触发复制,效率很高。但在多线程环境中,共享的地址空间让这一优化失效,成为了性能杀手。 为了解决这个问题,HHVM采用了一个巧妙的方案:提前创建一个代理进程池。当需要执行外部命令时,主线程通过管道将任务分派给处于单线程环境的代理进程,由后者去完成fork/exec操作。这样就将可能引发写时复制的操作,安全地隔离在了独立进程中,从根本上规避了性能陷阱。文章从实战故障出发,清晰揭示了多线程与操作系统机制交互时容易被忽视的深水区问题。

IT 累计浏览 3,282

lock free的理解

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

IT 累计浏览 23,520

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

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

IT 累计浏览 5,101

多核与异步并行

这篇讲的是如何通过异步并行编程技术来充分利用多核CPU,解决现代应用程序面临的延迟、吞吐量和响应度问题。 作者从一个经典矛盾切入:当程序调用耗时的I/O操作(如写文件)时,同步等待会让宝贵的CPU资源闲置。而异步调用允许调用线程立即返回继续工作,让耗时的任务在后台完成,从而“掩盖”了I/O延迟。 文章重点分析了GUI线程的异步并行设计,这是一个对响应度要求极高的场景。作者对比了三种将耗时操作(如保存、打印)从GUI线程转移出去的方式:使用一个专用工作线程顺序处理、为每个请求启动新线程并行处理,以及使用线程池来平衡资源利用与并行度。每种方式都附有清晰的示意图和伪代码,直观展示了其工作原理与权衡。 最后,文章以苹果的Grand Central Dispatch (GCD) 为例,说明了这一理念在现代平台上的成熟应用——开发者只需将任务块投入队列,系统便能自动利用多核资源进行高效调度。整体而言,这是一篇从原理到实践、讲解异步并行如何化阻塞为并发的技术入门好文。

IT 累计浏览 3,622

总结一下遇到过的网络同步IO导致服务阻塞的问题

这篇讲的是作者在工作中亲身遭遇的两个因同步IO引发服务阻塞的典型问题。第一个场景是fri_svr服务需要向第三方平台(如人人、Facebook)批量拉取用户数据,由于整个HTTP请求/响应周期过长(1s-10s),当并发请求量升高时,按user_id哈希分配的专用线程队列会发生堆积,直接导致服务内存暴涨并无法及时响应前端。 第二个场景则发生在使用ICE同步RPC的数据服务上。作者发现,某个线程队列中只要有一个任务(对应某个用户的请求)被意外阻塞,后续同队列的所有任务都会被拖累,导致部分用户响应延迟几分钟。而哈希到其他队列的用户则不受影响。 为了解决问题,作者将线程模型从“一个线程对应一个队列”调整为传统的线程池(多线程对应一个队列),从而避免了单点阻塞的连锁反应。但核心挑战在于保证同一用户(拥有相同owner)任务的执行顺序。作者设计了一个线程安全的数据结构:内部维护任务队列和一个KV表来记录每个owner是否正在被处理。任务被取出时,会检查并锁定owner状态,从而确保后续任务不会被乱序执行。 作者最后也指出,这种方案会引入额外的check/set开销与线程竞争。如果任务都是执行时间可控的CPU密集型任务,那么最初的一对一线程队列模型可能仍是更优选择。

IT 累计浏览 2,480

Transfer 2.0 介绍

这篇讲的是 Transfer 2.0 的全面升级,作者从实时

IT 累计浏览 3,422

MySQL多线程同步MySQL-Transfer介绍

这篇讲的是如何解决MySQL主从同步中的单线程瓶颈问题。作者介绍了一个基于MySQL 5.1打补丁而来的中间层工具MySQL-Transfer。 原生的MySQL主从复制中,从库单线程应用主库的binlog,在高并发写入场景下容易成为性能瓶颈。Transfer的核心方案就是引入多线程并行执行机制。它作为一个“虚拟从库”接收主库binlog,然后通过内部多线程并发地将更新应用到真正的从库上。其巧妙之处在于按照表名进行哈希,将不同表的更新分配到不同线程,从而在多表环境下显著提升同步吞吐量。此外,该方案还支持多主架构,可同时作为多个主库的从库进行同步。 从性能测试数据看,效果非常明显。在16个表并发各插入10万行数据的场景下,原生MySQL主从同步耗时363秒,而使用Transfer同步仅需66秒,平均TPS从约4400飙升至约24200,性能提升了数倍。文章还提供了具体的配置参数和应用补丁的方法,具有很强的实践指导性。

IT 累计浏览 4,000

锁是怎么实现的?

这篇讲的是锁在计算机底层的基本实现原理,作者从最基本的机制出发,梳理了实现锁的几种核心方式。 文章首先排除了应用层常见的各类复杂锁,聚焦于最底层的实现。它可能从原子操作说起,比如利用CPU的原子指令来保证单个操作的不可分割性,这是构建一切锁的基石。接着,会探讨更复杂的实现:比如自旋锁如何让线程在“忙等”中循环尝试获取锁,适用于极短临界区;而互斥锁或信号量则可能涉及内核态与用户态的切换,通过让线程挂起和唤醒来避免CPU空转,适用于可能耗时较长的场景。作者或许还会简要提及读写锁如何分离读写权限以优化并发性能。 这种从原理根源讲起的方式,帮助读者跳出了对“锁”这个抽象概念的模糊认知,理解了不同锁策略在性能、开销和适用场景上的根本权衡,为选择和设计正确的并发方案打下了基础。

IT 累计浏览 2,900

从Java视角理解伪共享(False Sharing)

这篇讲的是多线程并发编程中一个容易被忽略却影响巨大的性能陷阱——伪共享(False Sharing)。作者从Java的视角出发,深入解析了现代CPU缓存架构下的“缓存行”概念,以及当不同线程频繁修改位于同一缓存行的不同变量时,如何因缓存一致性协议(MESI)的无效化操作导致性能急剧下降。 文章对比了伪共享与“真共享”的区别,指出后者是开发者有意为之的数据共享,而前者则是无意中由内存布局引发的隐性竞争。作者通过JMH微基准测试,直观展示了在未做任何优化的情况下,存在伪共享的计数器累加操作吞吐量可能暴跌数十倍。核心解决手段包括通过对象填充(Padding)来确保关键变量独占缓存行,以及Java 8中引入的@Contended注解等底层优化方案。 对于从事高并发Java服务开发、需要极致性能优化的工程师来说,理解并识别伪共享问题是进行正确并发设计的关键一步。

IT 累计浏览 5,001

web开发框架的选择(bottle or flask)及为autumn增加多线程支持

这篇讲的是作者在 Python web 开发中,从 Django 转向轻量级框架 Bottle 后,针对项目 “autumn” 遇到的新挑战所进行的架构演进。作者在之前的实践中已为 Bottle 设计了合理的项目结构,但在后续运行中发现,单进程模型在处理耗时任务时会成为性能瓶颈。因此,本次实践的核心是为 autumn 系统增加多线程支持。 具体方案上,作者利用 Bottle 框架的灵活性,并未局限于其自带的开发服务器。通过引入 wsgiref 结合 threading 模块,为每个客户端请求创建并处理独立的线程,从而将 I/O 密集型或需要并行处理的任务解耦,避免阻塞主线程。文章详细记录了这一改造的实施过程与遇到的坑。 实验结果表明,这一调整显著提升了系统在并发场景下的吞吐能力和响应稳定性,使得 Bottle 这个原本极简的框架也能应对更复杂的生产环境需求。作者最终的结论是,在保持框架轻量优势的同时,通过合理的架构补充(如多线程),可以在灵活性和性能之间取得理想的平衡。

IT 累计浏览 8,963

大并发下的高性能编程 – 改进的(用户态)自旋锁

这篇文章聚焦于高并发系统中一个经典的性能瓶颈:锁竞争。作者从传统锁机制在极端并发下可能引发的严重性能问题出发,深入剖析了为何在用户态实现并优化自旋锁能成为一种有效的解决方案。 文章的核心是提出了一种改进的用户态自旋锁设计。它探讨了传统锁(可能涉及内核态切换)的开销,并详细阐述了在用户空间通过特定算法(如自适应自旋、结合无锁思想或对锁持有状态的精细判断)来实现更高效锁的思路。这个方案旨在避免或减少代价高昂的系统调用与上下文切换。 通过这种设计,文章展示的目标是在多核处理器、高竞争场景下,能够显著降低锁操作的延迟,并提升系统的整体吞吐量。这种对底层同步原语的极致优化,对于追求低延迟、高吞吐量的服务端开发具有直接的参考价值。

IT 累计浏览 4,920

Ameba , 一个简单的 lua 多线程实现

这篇讲的是作者基于 Lua 5.2 的一项新特性,实现了一个名为 Ameba 的轻量级多线程库。作者从 Lua 5.2 的协程改进出发,核心思路是利用协程来模拟线程,从而在 Lua 虚拟机内部实现一个协作式与抢占式相结合的调度模型。 具体来说,Ameba 允许用户像创建系统线程一样创建和管理 Lua 协程,但切换完全发生在虚拟机内部。它的巧妙之处在于,通过劫持和控制 Lua 虚拟机的执行点,在用户态实现了非对称协程的调度,让多个“线程”(即协程)可以并发执行,而无需依赖操作系统的线程机制。这既保留了 Lua 本身轻量、高效的优势,又为需要并发逻辑的场景提供了一个相对简单的解决方案。 文章的落脚点在于展示这种设计的简洁与实用性,它让开发者可以用熟悉的方式组织并发代码,同时底层机制完全透明可控。

IT 累计浏览 5,401

php多线程扩展

这篇讲的是作者用C语言动手写了一个PHP多线程扩展的实践。作者从社区中关于PHP能否以及是否需要多线程的争论出发,指出既然PHP内核是C,理论上C能实现的功能PHP也能触及。因此,他编写了一个相对简单的扩展,核心思路是创建与退出线程。 为了兼顾服务器性能,扩展设置了线程数上限,即当前CPU核心数的两倍。文中给出了创建线程的基础代码示例,主要面向的是有类似需求、想进行底层探索的开发者。这种直接动手验证想法的路径,为理解PHP与操作系统线程的交互提供了非常直观的参考。

IT 累计浏览 4,763

并行编程中的“锁”难题

这篇讲的是并行编程中一个经典又棘手的“锁”问题。作者从并发环境下多线程对共享资源的激烈争夺场景切入,重点对比了几种常用同步机制——互斥锁、自旋锁和读写锁的核心差异。 文章并没有停留在理论辨析,而是结合具体数据和性能剖析,点明了每种锁的适用边界。比如,互斥锁在竞争激烈时可能因频繁挂起带来开销;自旋锁则在短等待、低竞争场景下能减少线程切换的成本;而读写锁则通过“读共享、写独占”的策略,巧妙提升了读多写少场景的吞吐量。 作者的结论很清晰:没有“最好”的锁,只有最合适的锁。选择时必须权衡临界区长短、竞争激烈程度和读写比例等具体场景。这篇文章为开发者提供了一套在并发实战中评估和选择锁策略的清晰思路,帮助你更好地平衡正确性与性能。