进程和线程:单独工作与使用自传

📖 5min read

面试1分钟自我介绍等知识

“进程和线程有什么区别?”

我求职时,这个问题一直被列入“十大常见面试问题”。我像机器一样回答。

“进程是正在运行的程序,线程是在进程内执行的流程单元。进程不共享资源,但线程共享资源。”

面试官点点头,我想我完全理解这个概念了。然而,直到我在实践中遇到了“并发问题”,我才知道这句话所蕴含的真正恐惧。

“为什么浏览量应该增加 100,但实际上只增加了 98?” “为什么当我下载 Excel 时网站会停止?”

当我单独运行时,我的代码是完美的,但是当多个用户同时进来时,它就变得一团糟。我以为简单地增加‘worker’的数量就可以解决这个问题,但我不知道,随着worker数量的增加,管理它们(上下文切换)的成本呈指数级增长。

当你独自工作时,一切都是和平的,但当你与他人合作时,战争就开始了。

数字物流中心的工厂和工人

让我们回到“数字物流中心”的世界观。在计算机上运行程序就像在配送中心建立“工厂”。

1.流程:独立工作场所

2.主题:车间里的工人

线程同时拥有“共享空间(堆)”和“独立空间(堆栈)”。

【代码验证】共享的悲剧(并发问题)

“线程共享资源。”这在面试室里听起来可能是一个优势,但在实践中,它可能是“灾难”的种子。

还记得我们上次学过的‘堆’吗?线程共享这个堆区域。这意味着线程 B 可以覆盖线程 A 正在处理的数据。

这称为“竞争条件”。让我们用代码来检查一下。

public class RaceConditionTest {
    static int count = 0; // 儲存在堆(Heap)中的共享變數

    public static void main(String[] args) throws InterruptedException {
        // 僱用兩位工人(執行緒)
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) count++;
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) count++;
        });

        t1.start(); // 開始工作!
        t2.start(); // 開始工作!
        
        t1.join(); // 等待下班
        t2.join(); // 等待下班
        
        System.out.println("最終結果:" + count);
    }
}

预期结果:由于两个人相加 10,000 次,结果应该是 20,000

实际结果: 15,48218,931…每次运行的值都不一样,不显示20,000。

原因:

这就是实践中“有时数据被咀嚼”的bug的身份。这是当工人们在没有互相交谈的情况下触摸同一个账本时发生的悲剧。

如果我们将这种情况与数据库(DB)进行比较,它与“在没有事务的情况下同时进行多个查询的情况”相同。如果在没有锁定的情况下同时修改银行账户余额,则会发生资金蒸发的可怕事故。正如我们在数据库中使用 ROLLBACKCOMMIT 保护数据一样,在代码级别绝对需要诸如 Synchronized 之类的锁定设备。

实践中的权衡:多进程与多线程

那么在实践中你应该何时使用以及使用什么?

1.首选 Chrome 浏览器:多进程

在旧的 Internet Explorer 中,当一个选项卡停止时,整个浏览器都会关闭(多线程方法)。但是,Chrome 将每个选项卡作为“单独的进程(工厂)”启动。

2. Web 服务器的选择(Spring、Node.js 等):多线程

服务器必须处理数千个请求。如果为每个请求构建一个进程(工厂),服务器就会崩溃。因此,它是通过在一个进程中拥有多个线程(工作者)来处理的。

您愿意建造一座安全但昂贵的“堡垒”还是驾驶一架快速但危险的“无人机”?

结束语:自焚伴随着责任

今天我们研究了“进程和线程”,即工作人员的工作方式。

现在您知道“并发问题”一词不仅仅是一个面试术语。许多线程工作者不断地进出一个称为堆的共享仓库。在混乱中创造秩序是后端开发人员的技能。

但是等等,如果有很多工人,你如何决定谁应该先做谁的工作?工厂经理(OS)如何管理数百名工人?下次我们就来说说厂长最头疼的事情,“调度和上下文切换”。

發佈留言