漫谈多线程(上)

hey,you guys.

     好久不见了,最近忙着学习英文,处理一些杂事,所以没有来得及更新博客。公司目前没活,比较清闲。所以,有时间研究了一下<叩响C#之门>。据说作者是一位40多岁的初中数学老师,自学C#。40多岁的人自学编程,这份毅力很令人敬佩。这本书写的,是C#语言的基础知识。作者讲解的很清楚,读后很受益。很多之前一知半解,甚至一点都不懂得基础原理,现在豁然开朗。就像孔子所说:“朝闻道,夕死可矣。”这本书,真的适合初级.NET程序员读一下,真的很有帮助的。好了,书归正传,来开始我们今天的学习吧。

   多线程这块,在.NET中属于深层次的技术了。今天,我们就来学习学习多线程的知识吧。

线程的概念

大家平时使用电脑,可以一边听音乐,一边下载电影,一边浏览网页。那么电脑它是如何满足用户这种需求呢?原来操作系会创建三个应用程序,通过应用程序来执行用户的操作。每个应用程序被看作一条连续的指令流,CPU一条条执行这些指令流。但是早一些的电脑CPU不允许同一时间执行多条指令流,那么电脑又是如何做的呢?操作系统创建三个应用程序的同时,也创建了三个进程(Process)。进程不但包括了应用程序的指令流,还包括了运行应用程序所需的内存、寄存器等等。操作系统通过进程来执行应用程序。我们可以把进程看作是执行路线。操作系统 通过”时间片轮转”来轮流执行这些进程。所以,宏观上进程是并发执行的,在微观上是交替进行的。也就是说,电脑并不是同时满足听音乐、下载电影、浏览网页的,CPU是交替执行的,只不过交替时间很短暂,我们感觉不到。我们设想这样一个场景,一个网页播放Flash的同时,网页上还有一个文本框,来接受用户的输入。这个网页需要一边播放Flash,一边接受用户的输入。Note (个人理解,CPU执行这个网页的进程指令,同时这个进程还要执行播放Flash、接受用户输入这两个指令)。如果是以前,我们需要很复杂的一段代码来实现这个需求。但是多线程(Multi-Threading)技术的出现,就可以很容易的实现这个需求了。可以在网页这个进程中创建两个线程,通过这两个线程来执行播放Flash、接受用户输入。我们可以把进程中的多个线程,看作是多条执行路线。一个进程中的多个线程,它们共享资源。所以,线程之间的切换要远远快于进程之间的切换,我们可以把线程看作是轻量级的进程(LightweightProcess)。操作系统通过调度程序来管理线程,不需程序员关心,我们只需编写好线程即可,线程是CPU调度的基本单位。

Thread类

计算机运行某个应用程序时,会创建一个进程,进程会创建多个线程,通过线程来执行工作,我们把执行某些工作的线程称为工作线程(Work Thread)。C#中,关于线程的处理,都是通过System.Threading命名空间下的Thread类来完成的。

Using System.Threading;   //别忘了添加引用

//声明一个线程的代码
Thread workThread=new Thread(entryPoint);

entryPoint:是入口方法,线程会从入口方法的第一行代码执行。大家应该可以看出,Thread类的构造函数的参数类型是一个委托类型。也就是说entryPoint的函数的返回值、参数取决于Thread类的构造函数的委托参数决定。

//Thread类的构造函数参数的委托类型
public delegate void ThreadStart();

public delegate void ParameterizedThreadStart(object obj);

入口方法,必须是ThreadStart或ParameterizedThreadStart的委托。

那么综上所述,如果要创建一个线程,需要两步.

//第一步 创建入口方法
private void EntryPoint()
{
  //线程的具体代码
   ...........
    ...........
}

//第二步 创建线程对象
Thread workThread=new Thread(EntryPoint);

 

线程的优先级

我们工作生活中,需要处理很多事情。一般当紧的事儿需要及时处理,不当紧的事儿可放在后面处理。线程处理指令也是一样的,也分当紧与不当紧。通过Thread类的Priority属性来设置线程的优先级。Priority属性是一个ThreadPriority枚举。这个枚举有以下5个优先等级:

Normal、AboveNormal、Highest、BelowNormal、Lowest。

我们跑一下程序来测试一下线程优先级:

            //workThread1线程
            Thread workThread1 = new Thread(delegate()
            {
                for (int i = 0; i < 100000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write("A");
                    }
                }
            });

            //workThread2线程
            Thread workThread2 = new Thread(delegate()
            {
                for (int i = 0; i < 100000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write("B");
                    }
                }
            });

            //启动线程
            workThread1.Start();
            workThread2.Start();

运行程序,效果如下图:

我们通过效果图看以看出,两个线程基本是平均交替执行的,看不出优先级的高低。因为,默认的线程优先级是一般(Normal)级别的。下面,我们修改一下程序,改变线程的优先级:

             //workThread1线程
            Thread workThread1 = new Thread(delegate()
            {
                for (int i = 0; i <= 500000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write(‘A‘);
                    }
                }
            });

            //workThread2线程
            Thread workThread2 = new Thread(delegate()
            {
                for (int i = 0; i <= 500000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write(‘B‘);
                    }
                }
            });

            //修改Thread的优先级
            workThread1.Priority = ThreadPriority.AboveNormal;
            workThread2.Priority = ThreadPriority.BelowNormal;
            //启动线程
            workThread1.Start();
            workThread2.Start();

            //主线程代码
            for (int i = 0; i <= 500000000; i++)
            {

                if (i % 1000000 == 0)
                {
                    Console.Write(‘M‘);
                }
            }

 

其实除了workThread1、workThread2还有一个主线程(Main Thread)。我们修改了一下主线程的代码,以便观察三个线程是如何交叉工作的。我们代码中把workThread的Priority的属性设置为高于一般(AboveNormal)、把workThread2的Priority属性设置为低于一般(BelowNormal)。此时运行程序的效果如下图:

Note that:优先级高,只能证明占用CPU的时间长,并不代表,只有执行完优先级高的线程,才会执行优先级低的线程。在图中,我们也可以看出,B也回出现在A之前的。

 

线程插入

我们可以通过Thread类的Join()方法,将两个原本交替执行的线程,变为顺序执行。我们通过代码来观察这个Join()方法:

          static void Main(string[] args)
             {

            //workThread1线程
            Thread workThread1 = new Thread(delegate()
            {
                for (int i = 0; i <= 500000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write(‘A‘);
                    }
                }
            });

            //workThread2线程
            Thread workThread2 = new Thread(delegate()
            {
                for (int i = 0; i <= 50000000; i++)
                {
                    if (i % 1000000 == 0)
                    {
                        Console.Write(‘B‘);
                    }
                }
                workThread1.Join();
                for (int i = 0; i <= 50000000; i++)
                {
                    if (i % 10000 == 0)
                    {
                        Console.Write(‘b‘);
                    }

                }
            });

            //启动线程
              workThread1.Start();
            workThread2.Start();

        }

   我们在线程workThread2的入口方法中,调用了workThread1的join()方法,此时当程序执行到join()方法时,workThread2就会停止工作,去执行workThread1,直到workThread1执行完毕,才会接着执行workThread2。程序运行结果如下图:

Join()方法,还可以接受一个表示毫秒的参数,当达到这个时间,不论是否执行完毕,都会退出。

线程状态

线程一共有7种状态,它们分别是未开始状态(UnStarted)、运行状态(Running)、等待睡眠插入状态(WaitSleepJoin)、挂起请求状态(SuspendRequested)、挂起状态(Suspended)、中止请求状态(AbortRequested)、中止状态(Stopped)。大家可以通过下面这幅图片来认识这7中状态:

1.未开始状态(Unstarted)

当一个线程被创建,它的状态会变为未开始状态(UnStarted).

2.运行状态(Running)

当线程调用Start()方法时,线程状态就会变为运行状态(Ruuning)。

3.等待睡眠插入状态(WaitSleepJoin)

当运行状态的线程调用方法Sleep()、Join()、或Wait()时,线程状态会变为等待睡眠插入状态(WaitSleepJoin).此时如果调用Pulse()或Interrupt()线程状态会变为Running状态。

4.挂起请求状体(SuspendRequested)

当运行状态的线程调用方法Suspend()的时,线程状态会变为SuspendRequested。

5.挂起状态(Suspended)

当调用Suspend()方法时,线程不会立马被挂起,而是会处于SuspendRequested状态,线程会再执行几条指令,当确保线程在一个安全的状态下,挂起线程,线程状态变为Suspended。调用Resume()方法可以使线程状体变为Running状态。

6.中止请求状态(AbortRequested)

当处于运行状态的线程调用方法Abort()时,线程状态会变为AbortRequested。

7.中止状态(Aborted)

当线程调用Abort()方法时,线程不会马上中止,而会处于AbortRequested状态,再执行几条指令,当确保线程在一个安全状态下,中止线程,线程状态变为Stopped。

时间: 2024-07-30 05:50:23

漫谈多线程(上)的相关文章

Java 多线程(上)

启动一个多线程 多线程即在同一时间,可以做多件事情,创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类 线程概念 首先要理解进程(Processor)和线程(Thread)的区别进程:启动一个LOL.exe就叫一个进程. 接着又启动一个DOTA.exe,这叫两个进程.线程:线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦" 击杀"提莫",同时"赏金猎人"又在击杀"盲僧",这就是

漫谈多线程(一)

一.多线程这里的东西个人觉得整体的设计灵感来自于生活,多线程的目的是为了提高程序运行的效率,映射到我们生活中,是能找到异曲同工之妙的. 二.多线程提的比较的多的概念可能就是同步和异步了,什么是同步,简单说就是前面的任务没完成的话不执行后面的任务,异步就是前面的任务就算没执行完,后面的任务也开始执行.比如生活中,我们下班回家了,需要做饭,洗衣服,拖地.同步的方式就是一.先用电饭锅开始煮饭,花了10分钟,我们看着电饭锅把饭煮熟,二.然后再去用洗衣机洗衣服,花了10分钟,看着洗衣机把衣服洗完,三.再来

漫谈多线程(下)

接着上一篇继续学习多线程. 死锁(DeadLock) 当多线程共享资源时,各占一部分资源,而又在等待对方释放资源,这样的情况我们称为死锁.下面通过一个生动的程序来理解死锁. class Program { private static object knife = new object(); //临界资源:刀子 private static object fork = new object(); //临界资源:叉子 //方法:拿起刀子 static void GetKnife() { Conso

《Inside C#》笔记(十三) 多线程 上

通过将一个任务划分成多个任务分别在独立的线程执行可以更有效地利用处理器资源并节省时间.但如果不合理地使用多线程,反而会带来种种问题并拖慢运行速度. 一 线程基础 a)线程与多任务 一个线程就是一个处理单元,多任务时多个线程会同时执行.多任务时会涉及到任务间的合作与优先级的问题.Windows NT内核的操作系统使用抢占多任务处理机制(preemptivemultitasking),系统会为每个线程划分出确定的执行时间(时间片),然后线程在给定的时间片内轮流执行. 在单核处理器上使用多线程时,线程

C#多线程(上)

一.多线程的相关概念 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源. 而一个进程又是由多个线程所组成的. 什么是线程? 线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数. 什么是多线程? 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务. 多线程的好处: 可以提

漫谈多线程(中)

这一篇接着上一篇来继续学习多线程. 线程同步 在大多数情况下,计算机中的线程会并发运行.有些线程之间没有联系,独立运行,像这种线程我们称为无关线程.但也有一些线程,之间需要传递结果,需要共享资源.像这种线程,我们称为有关线程.比如,我们网上观看电影,一个线程负责下载电影,一个线程负责播放电影.它们只有共同合作我们才能观看到电影,它们之间共享资源.由此,我们可以看出,线程的相关性体现在对同一资源的访问上.我们把这种供多个线程访问的资源成为临界源(Critical Resource).访问临界源的代

java 多线程上传解压文件

举个公司项目开发遇到的一个简单例子,用户上传压缩文件到服务器后,要对该压缩包进行两个操作,一是将该压缩包复制到指定目录,一是将该压缩包解压到另一指定目录,最终响应用户提示文件上传成功.如果压缩包很大的话,上传后进行的复制和解压功能也会占用很长时间,用户就会等待很长的时间.其实复制和解压的功能和用户操作没有直接关系,完全可以独立出来,其解决的思路如下: 当用户上传压缩文件完毕之后,我们立即创建两个线程,一是复制压缩文件的线程:二是解压压缩文件的线程.我们可以通过线程的构造方法把文件的信息传递给相应

多线程(上)

引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发.其中委托的BeginInvoke方法以及回调函数最为常用.而 I/O线程可能容易遭到大家的忽略,其实在开发多线程系统,更应该多留意I/O线程的操作.特别是在ASP.NET开发当中,可能更多人只会留意在客户端使用Ajax或者在服务器端使用UpdatePanel.其实合理使用I/O线程在通讯项目或文件下载时,能尽可能地减少IIS的压力.并行编程是Framework4.0中极力推

Python基础 - 多线程(上)

前面对 进程 一点认识, 通俗理解, 进程是操作系统(OS)进行资源调度分配的基本单元. 每个程序的至少就一个进程在OS中被"监控"着的哦. 然后围绕着多进程, 用消息队列共享全局变量, 守护主进程, 进程池...这一通探讨, 当然还是偏向应用的一方, 我自己偶尔工作有多任务的处理的地方, 也是优先弄多进程 (主要是公司电脑贼强, 我就要弄多进程, 就要浪费资源哈哈..). 进程 呢, 基本没用过, (爬虫除外, 之前有用 scrapy 是多线程的), 自己来手写应该是没有的. 为啥宁