现代语言怎么支持Parallel和Concurrent

Parallel和Concurrent 我们先来回顾一下概念: Parallel是指同时执行多个任务或计算.通常在多核或多处理器的硬件上并行执行多个操作,每个操作独立运行,并且是同步的(并行的任务在同一时刻发生).并行计算的目标是通过同时使用多个计算资源来加速程序的执行. Concurrent是指系统在同一时间段内处理多个任务的能力.并发并不意味着任务是同时执行的,而是任务在某一时刻轮流执行. 举个例子就更容易理解: 你有一个奶茶店,制作奶茶需要三个步骤,分别是收银,调制奶茶配料和用设备制作奶茶.假设现在有两个角色,A负责收银,B负责调制奶茶配料和用设备制作奶茶.整个步骤是A->B.作为店主你很快发现A收银很快,B调制配料和制作奶茶很慢造成了瓶颈,这是你选择解决方案是再雇佣了另外一个人和B一起负责调制奶茶配料和用设备制作奶茶,现在整个步骤是A->B1 or B2.在这个场景中增加B2就是我们说的Parallel.当然你还可以用另一个方案解决,把原步骤调制奶茶配料和用设备制作奶茶拆分为两个步骤,分别让B负责调制奶茶配料,再雇佣一个人去负责用设备制作奶茶,步骤变为A->B->C,在这个场景中增加C就是增加了Concurrent. 现代语言的并发模型 现代语言支持并发模型一般会在操作系统线程和应用程序之间增加一层,在这层中使用预先分配的线程池和调度器来实现应用程序的并发能力.如在Go语言中称为协程(Coroutine). Go语言的并发模型如下: G—Goroutine M—OS thread (stands for machine) P—CPU core (stands for processor) 默认情况每一个CPU核心(P)里面会有一个线程(M),每个线程会不断切换去运行协程(G). Swift语言的并发模型,同样也是在操作系统线程和应用程序之间增加了一层.稍有不同的是他通过把代码划分为逻辑单元称为局部任务(Partial Tasks),通过调度器把局部任务分配到线程池不同的线程运行来实现并发能力. 现代语言中支持并发的语言特性 那么在这些语言中怎么支持并发的呢?这里我们举例三个场景: 场景1: 增加Parallel,就是我们前面说的第一个方案增加另一个人和B一起负责调制奶茶配料和用设备制作奶茶 Go实现 func taskA() int { time.Sleep(2 * time.Second) return 5 } func parallelExample() int { c := make(chan int) go func() { c <- taskA() }() go func() { c <- taskA() }() r1 := <-c r2 := <-c return r1 + r2 } Swift实现 ...

January 2, 2025

SwiftUI @State、@Observable、@Binding、@Bindable实现原理

@State 用途 struct 1.私有的View State,使用当前View维护状态生命周期 2.保持struct不可变性的同时修改内部变量值 class 1.私有的View State,使用当前View维护状态生命周期 使用方式 struct Counter: View { @State private var value = 0 var body: some View { Button("Increment: \(value)") { value += 1 } } } 内部实现 struct Counter: View { private var _value = State(initialValue: 0) private var value: Int { get { _value.wrappedValue } nonmutating set { _value.wrappedValue = newValue } } var body: some View { Button("Increment: \(value)") { value += 1 } } } SwiftUI给value state在render tree里面分配内存并赋予initialValue:0,并建立链接使value指向这个内存值 Counter’s body依赖这个内存值,一旦内存值变化会重新构建Counter’s body. ...

November 29, 2024

编译器是否帮我们做的过多?

近段时间编程语言开始往安全方面进行发展,类型安全,内存安全,并发安全等,如新兴的Rust语言,Swift 6.0都在安全性上发力,并都在编译器方面下功夫,添加了诸多规则,当你和编译器战斗通过后,理论上你的代码具备某种程度的安全性。 这类安全性同时也影响着语言的发展,比如在面向接口编程中Swift语言里面对于接口还增加了some和any两个关键字,一时让我疑惑不解,其他语言没有这类概念为啥这个语言需要,他想要解决了什么问题?然后又引入两个类型概念Opaque Types和Boxed Protocol Types。 官方文档描述如下: “You can think of an opaque type like being the reverse of a generic type. … An opaque type lets the function implementation pick the type for the value it returns in a way that’s abstracted away from the code that calls the function. … An opaque type refers to one specific type, although the caller of the function isn’t able to see which type” ...

November 19, 2024

程序语言中内存管理-DO NOT STOP THE WORLD

类似枪械有手动步枪-M1903春田,半自动步枪-M1,自动步枪-M16之分,程序语言的内存管理也有手动、半自动和全自动三种方式。手动内存管理的代表语言:C、C++,半自动内存管理的代码语言:Modern C++,Swift,Rust,全自动的代表语言:Java,C#,Go,Python。本文通过不同程序语言来描述三种内存管理方式,以及如何应对循环引用的典型场景。 手动内存管理: 为了实现代码的极致效率以及灵活性C和C++语言采用了手动内存管理方式,通过自己编写代码实现堆上的内存分配与回收。 //C++代码示例 export class Manual { public: Manual(int s) :elem{ new double[s] }, sz{ s } {} ~Manual() { delete[] elem; } private: double* elem; int sz; }; 半自动内存管理: 内存泄漏是手动内存管理遇到最大的挑战,在复杂的场景下可能出现分配的内存没有释放,导致程序在特殊的情况下内存耗尽停止工作。为了保证内存安全,新一代语言为了不牺牲效率采用编译时期来分析内存的分配与释放,典型的方式是自动引用计数(Automatic Reference Counting ) //swift代码示例 class ARC { } func usingARC() { var ref1: ARC? = ARC() var ref2: ARC? = ref1 ref1 = nil ref2 = nil //ref1、ref2 已释放 } //Modern C++ export class SmartPointers { public: //represents unique ownership (its destructor destroys its object) void uniquePtr() { std::unique_ptr<X> sp{ new X }; std::unique_ptr<X> sp3 = std::make_unique<X>(); std::unique_ptr<X> sp2 = std::move(sp); } //represents shared ownership (the last shared pointer¡¯s destructor destroys the object) void sharedPtr() { std::shared_ptr<X> sp = std::make_shared<X>(); std::shared_ptr<X> sp2 = sp; sp.reset(); sp2.reset(); } //A pointer to an object owned by a shared_ptr void weakPtr() { std::shared_ptr<X> sharedPtr = std::make_shared<X>(); // Creating a weak pointer from the shared pointer std::weak_ptr<X> weakPtr = sharedPtr; // Using the weak pointer to access the object if (auto ptr = weakPtr.lock()) { } else { std::cout << "Weak pointer is expired." << std::endl; } // Resetting the shared pointer sharedPtr.reset(); // Using the weak pointer again after resetting the shared pointer if (auto ptr = weakPtr.lock()) { } else { std::cout << "Weak pointer is expired." << std::endl; } } }; 全自动内存管理: 同样保证内存安全,同时减少内存管理心智负担,还有一种全自动内存管理-垃圾回收机制(GC)。但这种方式引入了臭名昭著的STOP THE WORLD。 ...

March 26, 2024

编程语言,群魔乱舞的时代

自从AI统治了世界,编成语言宝座一直由Python占据,但近几年编程语言热度排名变化,让我感到有些奇怪。 在TIOBE中23年不出意外C#成为年度语言,22年年度语言C++,Java跌出前三,24年很可能被C#反超,Rust进入前20。 同时这几年新一代语言层出不穷,Mojo在AI领域挑战python,名不见经传的zig语言写出个Bun准备在js领域掀起浪花,Rust对C++发起冲击多年。不禁在想老一代语言除了生态的保护,还有什么能应对新一代语言的挑战。 C++98,03是C++的黄金时代,后来随着Java,C#成长发展,C++领域逐渐萎缩。处境一度变得十分尴尬,在企业级市场Java解决了内存管理问题变得流行,在嵌入式领域C语言有简单性和性能上的优势,还剩下游戏领域的引擎部分还是C++一家独大。 当时C++遇到的问题: 复杂性,太过于复杂导致编码的认知复杂度太高,C++是一把屠龙宝刀,威力太大,用好他需要专业程序员,多年的沉淀,特别10倍程序员,市场上这类人才太少,中国企业大多急功近利不愿培养。 内存泄漏,内存安全问题突出。在复杂业务逻辑下,遗漏内存释放,程序长时间运行后崩溃,定位这类问题难度很大。 标准库不完善,三方库种类繁多,不像Java的Spring全家桶,不同的程序,用不同的三方库,维护困难。你想象一下几十年的语言还没有统一的并发库就知道了。所以像腾讯这类使用C++为主都需要自建所有的基础库。 但为什么近几年C++热度突然上升,我想可能是因为: AI火热,Python虽然是AI领域使用最广泛的语言,但一般用于模型训练这类,但AI的核心机器学习框架就只有C++能胜任了 C++自从03后加速发展,在进一步提升复杂度上一去不回,随后加入线程,协程,所有权,模块化等等等等,和Go语言设计理念完全相反 硬件摩尔定律失效,3nm技术再往下继续发展,投入成本和时间都急剧攀升,CPU单核频率提升到达瓶颈,市场又开始关注怎么充分利用有限的硬件资源 网上有人说C++03是“经典C++(classic)”,C++11开始是“现代C++(modern)”,那么C++吸取了哪些有意思的东西,准备折腾一番

January 5, 2024