关于多线程白话版

多线程是程序员面试时常常会面对的问题,对多线程概念的掌握和理解水平,也会被一些老鸟用来衡量一个人的编程实力的重要参考指标。不论是实际工作需要还是为了应付面试,掌握多线程都是程序员职业生涯中一个必须经过的环节。其实当你把“多线程”和你的“职业生涯”联系在一起考虑的时候,就会觉得“多线程”是多么的渺小,对,没有跨越不过的山。不过就算它很渺小,但也有可能改变你的人生轨迹。不用担心,如果你对多线程还不太熟悉,那么我们就一起来看看什么是多线程吧。

跟前几篇的风格一样,我会在开篇的时候举一个现实生活中的例子,通过这个例子来映射一些晦涩枯燥的计算机编程专业知识,在让读者朋友很好地理解理论概念的同时,又避免了阅读教科书时的枯燥感觉。这次我要举的例子是公司。不一定是IT公司,尽量和编程领域远一点儿吧,那就假设是一家搬家公司吧。

假如我们把公司看做是一个进程,那么人就是其中的线程。进程必须得有一个主线程,公司在创业初期往往可能出现一人打天下的现象,但是,至少得有一个人,公司才能运作。公司创业初期,业务还不算太多,往往就是老板一个人身兼数职,一天如果只有1、2趟活儿,应该还是忙得过来的。时间长了,随着业务的发展、口碑的建立,生意越来越兴隆,一个人肯定就忙不过来了。假设一天有5个活儿,老板一个人必须搬完A家才能搬B家,搬到黄昏估计也就搬到C家,D和E家都还在焦急地等待着呢。老板一个人要充当搬运工、司机、业务联系人、法人代表、出纳等众多角色,累死累活公司的规模也上不去,人手不够制约了公司的发展。那么怎么办,很简单,增加人手,用编程的话来说就是“再起个线程”。

我们现在就用代码来描述这样的场景吧,首先,我们准备成立一家搬家公司,于是要准备好将来和客户签的合同书:

public class Contract
{
    public string ID { get; private set; }
    public string From { get; set; }
    public string To { get; set; }
    public decimal Fee { get; set; }

    public Contract()
    {
        this.ID = Guid.NewGuid().ToString()
    }
}

简是简单了点儿,好歹也是份合同,现在我们就去申请注册一家公司,并组建好初创团队,哪怕目前还只有老板一个人:

public class HouseMovingCompany
{
    private static HouseMovingCompany _instance = null;
    public static HouseMovingCompany Instance
    {
        get { return (_instance == null ? _instance = new HouseMovingCompany() : _instance); }
    }

    public List<Contract> Contracts { get; private set; }

    public HouseMovingCompany()
    {
        this.Contracts = new List<Contract>();
    }

    public void MoveHouse()
    {
        if (this.Contracts == null || this.Contracts.Count == 0)
        {
            return;
        }

        Contract contract = contract = this.Contracts[0];
        this.Contracts.RemoveAt(0);

        if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
        {
            Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
        }

        Thread.Sleep(5000);
    }
}

好了,现在公司实体有了,老板就可以开始忙活了:

static void Main(string[] args)
{
    HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });

    while (HouseMovingCompany.Instance.Contracts.Count > 0)
    {
        HouseMovingCompany.Instance.MoveHouse();
    }
}

我们在前面设置了每次搬家耗时5秒钟,咱们把它想象成5个小时。嗯,一天接一个单子,还可以接受,但是随着老板生意日渐兴隆,有时候一天要接3个单子,这就最少要工作15个小时了,还要操心公司的运营等问题,的确忙不过来了,而且照这样算,老板一天不可能完成5个或5个以上的单子,严重制约了公司的发展:

static void Main(string[] args)
{
    HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
    HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
    HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });

    while (HouseMovingCompany.Instance.Contracts.Count > 0)
    {
        HouseMovingCompany.Instance.MoveHouse();
    }
}

一天夜里,老板拖着疲倦的身子回到家里,一进门就一头倒在床上,他极力睁着快睁不开的眼睛,努力地对自己说:“不行,我一定要想个办法,不然我会被累死的!”。

其实办法很简单,谁都知道,招聘几个员工,再买几辆车,大家分头行动,不仅分担了工作负担,而且在规模扩大的同时还可以完成更多更大的单子。好,我们现在就借助多线程机制来实现我们的想法:

 static void Main(string[] args)
 {
     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
     HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });

     Thread thread = null;

     while (HouseMovingCompany.Instance.Contracts.Count > 0)
     {
         thread = new Thread(new ThreadStart(HouseMovingCompany.Instance.MoveHouse));

         thread.Start();
     }
 }

在这段程序中,我们分头行动,让每项搬家任务都由一个小团队去完成,结果我们发现,现在做三个单子的时间跟做一个单子的时间是一样的,提高了效率也扩大了公司规模。但是,既然引入了新的工作机制,我们在公司内部也不得不做一些小小的调整:

public void MoveHouse()
{
    if (this.Contracts == null || this.Contracts.Count == 0)
    {
        return;
    }

    Contract contract = null;

    lock (this.Contracts)
    {
        contract = this.Contracts[0];
        this.Contracts.RemoveAt(0);
    }

    if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
    {
        Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
    }

    Thread.Sleep(5000);
}

调整的只是MoveHouse这个方法内部的一些实现细节。公司接到的单子都保存在Contracts中,所以搬家的时候需要去拿一个单子然后根据单子上的信息来工作。原先我们只有一个线程在操作Contracts,倒也不觉得什么,现在有多个线程都在对Contracts中的元素进行存取,我们不得不提防一些意外发生。这就是在使用多线程的时候常常需要考虑的并发问题,所以我们用了lock关键字,当一个线程要操作Contracts时,它先把Contracts锁起来,其实就是声明一下:“现在我在操作它,你们谁都不要动,等我弄完了再说。”在lock块结束时被锁定的对象才会被解锁,其它的线程现在才可以去操作它。

有了多线程机制,你会发现程序可以在更短的时间内完成更多的事情。本文没有将多线程机制中的所有概念面面俱到地列举出来,但是已经向你展示了该如何使用多线程以及什么时候可以考虑使用多线程,其它的一些细节有待你去进一步探索,例如,你可以设置线程的优先级(假设逻辑上跟Fee挂钩,类似于‘加急’)等等。

掌握多线程机制,并让它使你的应用程序变得更加强悍吧。

时间: 2024-11-05 03:48:02

关于多线程白话版的相关文章

java多线程--“朴素版”生产者消费者问题

1. 生产/消费者模型 生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括"生产者"."消费者"."仓库"和"产品".他们之间的关系如下: (01) 生产者仅仅在仓储未满时候生产,仓满则停止生产. (02) 消费者仅仅在仓储有产品时候才能消费,仓空则等待. (03) 当消费者发现仓储没产品可消费时候会通知生产者生产. (04) 生产者在生产出可消费产品时候,应该通知等待的消费者去消费. 2. 生产/消费者实现 下面通过

多线程 简洁版

多线程技术分享 背景: 以前单CPU,单任务的条件下,一段时间只能执行单一的程序.之后发展到多任务阶段.多个任务共享一个CPU,本质上是有操作系统来完成CPU对多个用户的切换.保证每个任务都有一定的时间片来完成任务. 1.      概念和原理 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.线程总是属于某个进程,进程中的多个线程共享进程的内存. 2.      线程的两种实现方法 1.

神经网络的白话版

什么是卷积神经网络?为什么它们很重要? 卷积神经网络(ConvNets 或者 CNNs)属于神经网络的范畴,已经在诸如图像识别和分类的领域证明了其高效的能力.卷积神经网络可以成功识别人脸.物体和交通信号,从而为机器人和自动驾驶汽车提供视力. 在上图中,卷积神经网络可以识别场景,也可以提供相关的标签,比如"桥梁"."火车"和"网球":而下图展示了卷积神经网络可以用来识别日常物体.人和动物.最近,卷积神经网络也在一些自然语言处理任务(比如语句分类)上

图形学:图像围绕着某个点P(a,b)旋转------白话版

前提:在研究图形时候,我们并没有规定图形的大小,所以任意图形多是支持的,这也另外说明了一点,图形转换和图形的大小没有关系. 如果图像围绕着某个点P(a,b)旋转,则先要将坐标系平移到该点,再进行旋转,然后将旋转后的图像平移回到原来的坐标原点. -----这句话的解读,以前我一直以为是将坐标系平移到图像的中心....其实不是,因为这里并没有规定图像大小,所以只是平移到旋转点.看仔细点..还有一点不要将物体想象为某一个具体小方块. 这是一直误解,因为旋转物体是支持任意物体旋转的,所以应该理解物体无线

几种常用设计模式的白话版

工厂模式:根据不同的情况实例化不同的对象. Factory 抽象工厂模式:接口来创建对象工厂,生成的工厂利用工厂模式创建对象. 产品族. QQ换肤,切换操作系统,切换数据库(比如Oracle和SQLserver操作一个表 或者登陆操作). 单例模式:所有的线程都操作同一个对象时,这个对象必须是唯一的实例. 全局的.Singlton 代理模式:增加对对象的控制,比如携程卖火车票.桌面快捷方式.在代理类有个对象的实例化,可以直接用这个对象.Proxy 外观模式:在外观类里实例化对象,然后调用对象方法

rsyslog 详解3

http://www.centosabc.com/archives/601 http://www.cnblogs.com/aguncn/p/4249175.html rsyslog服务和logrotate服务======================================================================rsyslog 是一个 syslogd 的多线程增强版.现在Fedora和Ubuntu, rhel6默认的日志系统都是rsyslog了rsyslog负责

VC++ 编译过程

一 前言 以前在编译C++代码的时候对编译的错误都觉得很难理解,搞不清楚究竟是哪里错了.后来C++写多了,总结了一些经验,然后也了解了一下编译过程,但是没有系统的对编译过程进行学习,现在趁着写博客的机会,好好的把编译过程写下来,并且理顺一下这个知识点. 二 名词解释 编译单元:当一个c或cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有 必要信息的单个源文件,这个源文件就是一个编译单元. 目标文件:目标文件包含着机器代码(可直接被计算机中央处理器执行)以及代码在运行时使用的数据,此

Hibernate框架简介

面试被问及了Hibernate框架,虽然问的很少,很简单,但是还是简单的总结一下吧,以备以后不时之需. 什么是Hibernate框架? 百科定义:Hibernate框架式一个开源的对象关系映射(ORM)框架,是对JDBC的轻量级的对象封装,使java程序员可以使用对象思维来操纵DB. 白话版:在Hibernate之前,我们是如何操作DB的?JDBC,需要在程序中嵌入SQL语句.效率低下,不能忍啊.但是Hibernate的出现,让我们无需再操纵SQL语句了,每个表实例被实例化为一个java对象,操

屏幕尺寸,分辨率,像素,PPI之间到底什么关系?

转载自:http://www.jianshu.com/p/c3387bcc4f6e 感谢博主的无私分享. 今天我给大家来讲讲这几个咱们经常打交道的词到底啥意思,以及他们之间到底有什么关系.这篇文章是我花了一个下午从N多篇文章里提炼出的一个白话版,保证让你看得懂. 咱们从手机开始说起吧.先上一张图,给大家看看关于手机屏幕方面的一些参数.红框内的三个参数,大家一定都不陌生,我也不陌生.不过讲真的,就在不久前,我连手机的屏幕尺寸到底是怎么算出来的都不知道.下面我们开始慢慢讲. 屏幕(主屏)尺寸是什么,