C#多线程编程实战(原书第2版)(txt+pdf+epub+mobi电子书下载)


发布时间:2021-03-03 16:42:06

点击下载

作者:(美)易格恩·阿格佛温(EugeneAgafonov)

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

C#多线程编程实战(原书第2版)

C#多线程编程实战(原书第2版)试读:

前言

不久前,典型的个人计算机的CPU还只有一个计算核心,并且功耗足以煎熟鸡蛋。2005年,英特尔推出了其首款多核心CPU,从此计算机开始向不同的方向发展。低耗电量及多个计算核心变得比提高行计算(row computing)的核心性能更重要。这也导致了编程范式的改变。现在我们需要学习如何有效地使用所有CPU核心来最优化性能,并同时通过在特定时间只运行需要的程序来节省电池电量。除此之外,我们在编写服务器端应用程序时需要有效地利用多个CPU核心,甚至多台计算机来支持尽可能多的用户。

为了创建这样的应用程序,你需要学习如何在程序中有效地使用多个CPU核心。如果你使用的是Microsoft.NET开发平台以及C#编程语言,那么本书将是一个编写高性能、高响应性的应用程序的完美起点。

本书的目的是给你提供C#中多线程以及并行编程的详尽指导。我们将从基本概念开始,每章主题比前一章都有所拔高,最后展示现实世界中的并行编程模式以及通用Windows应用和跨平台应用示例。本书内容

第1章介绍了C#中基本的线程操作。本章解释了什么是线程,使用线程的优缺点,以及与线程相关的其他重要方面。

第2章描述了线程交互细节。你将了解为何我们需要协调线程,以及协调组织线程的不同方式。

第3章解释了线程池概念。本章展示了如何使用线程池,如何执行异步操作,以及使用线程池的好的和不好的实践。

第4章深入讲解了任务并行库(Task Parallel Library,TPL)框架。本章讲述了TPL的所有重要方面,包括任务组合、异常管理及取消操作等。

第5章深入解释了最近引入的C#特性——异步方法。你将了解async和await关键字,如何在不同的场景中使用它们,以及await的底层工作机制。

第6章描述了.NET框架中并行算法的标准数据结构,并为每种数据结构展示了示例编程场景。

第7章深入讲解了并行LINQ基础设施。本章讲述了任务和数据并行度、并行化LINQ查询、调整并行选项、分割查询和集合并行查询结果等内容。

第8章解释了如何以及何时使用Reactive Extensions框架。你将学习如何组合事件,如何对事件序列执行LINQ查询等。

第9章深入讲解了异步I/O进程,包括文件、网络及数据库等场景。

第10章列出了针对常见的并行编程问题的解决方案。

第11章讲述了为Windows10、OS X,以及Linux编写异步应用程序。你将学习如何使用Windows 10异步API,以及如何在Windows商店应用中完成后台工作。你也会熟悉跨平台的.NET开发工具和组件。准备事项

我们需要Microsoft Visual Studio社区版2015来完成大多数章节的学习。在第11章中,对于OS X和Linux可能需要Visual Studio Code编辑器,当然你也可以使用任何你熟悉的编辑工具。读者对象

本书的读者对象为没有或只有少量多线程及异步和并发编程背景的C#开发人员。本书涵盖了C#和.NET生态系统中从基础概念到复杂编程模式及算法的很多与多线程相关的主题。下载示例代码

本书的示例代码可登录华章网站(http://www.hzbook.com)中的本书页面下载。第1章 线程基础

本章将涵盖C#中使用线程的基本操作。

你将学到以下内容:

·使用C#创建线程

·暂停线程

·线程等待

·终止线程

·检测线程状态

·线程优先级

·前台线程和后台线程

·向线程传参

·使用C#中的lock关键字

·使用Monitor类锁定资源

·处理异常1.1 简介

过去普通计算机只有一个计算单元,不能同时执行多个计算任务。然而操作系统却已经可以同时运行多个应用程序,即实现了多任务的概念。为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式将物理计算单元分割为一些虚拟的进程,并给予每个执行程序一定量的计算能力。此外,操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级。线程正是这一概念的实现。可以认为线程是一个虚拟进程,用于独立运行一个特定的程序。请记住线程会消耗大量的操作系统资源。多个线程共享一个物理处理器将导致操作系统忙于管理这些线程,而无法运行程序。

因此,虽然有可能提高计算机的处理器能力,使得计算机每秒钟能执行越来越多的命令,但是使用线程通常是一个操作系统任务,试图在单核CPU上并行执行计算任务是没有意义的,因为这比顺序运行会花费更多的时间。然而,当处理器拥有多核时,过去的应用程序则不能使用这个优势,因为它们只使用了一个处理核心。

为了有效地利用现代处理器的计算能力,使用某种方式让程序能使用不止一个处理核心是非常重要的。这需要组织多个线程间的通信和相互同步。

本章中的内容将关注于使用C#语言执行一些非常基本的线程操作。我们将介绍线程的生命周期,其包括创建线程、挂起线程、线程等待,以及中止线程。然后我们会介绍一些基本的线程同步技术。1.2 使用C#创建线程

在接下来的内容中,我们将使用Visual Studio 2015作为主要的工具来使用C#编写多线程程序。本节将展示如何创建一个新的C#程序,并在该程序中使用线程。从微软官方网站可以下载免费的Visual Studio社区版2015。我们需要它来运行示例代码。1.2.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe1目录中。1.2.2 实现方式

请执行以下步骤来了解如何创建一个新的C#程序,并在其中使用线程:

1.启动Visual Studio 2015。新建一个C#控制台应用程序项目。

2.确保该工程使用.NET Framework 4.6或以上版本。不过本节的代码在之前的版本中也能正常运行。

3.在Program.cs文件中加入以下using指令:

4.在Main方法下面加入以下代码片段:

5.在Main方法中加入以下代码片段:

6.运行程序。输出如下所示:1.2.3 工作原理

步骤1和步骤2创建了一个C#的简单的控制台应用程序,使用了.Net Framework 4.0版本。步骤3引用了命名空间System.Threading,该命名空间包含了该程序用到的所有类型。我们使用了C#6.0提供的using static特性,可以使用System.Console类型的静态方法而不用我们指定类型名。正在执行中的程序实例可被称为一个进程。进程由一个或多个线程组成。这意味着当运行程序时,始终有一个执行程序代码的主线程。

步骤4中定义了方法PrintNumbers,该方法会被主程序和新创建的线程使用。在步骤5中创建了一个线程来运行PrintNumbers方法。当我们构造一个线程时,ThreadStart或ParameterizedThreadStart的实例委托会传给构造函数。我们只需指定在不同线程运行的方法名,而C#编译器则会在后台创建这些对象。然后我们在主线程中以通常的方式启动了一个线程来运行PrintNumbers方法。

结果两组范围为1到10的数字会随机交叉输出。这说明PrintNumbers方法同时运行在主线程和另一个线程中。1.3 暂停线程

本节将展示如何让一个线程等待一段时间而不用消耗操作系统资源。1.3.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe2目录中。1.3.2 实现方式

请执行以下步骤来了解如何暂停线程而不消耗操作系统资源:

1.启动Visual Studio 2015。创建一个新的C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.3.3 工作原理

当程序运行时,会创建一个线程,该线程会执行PrintNumbersWithDelay方法中的代码。然后会立即执行PrintNumbers方法。关键之处在于在PrintNumbersWithDelay方法中加入了Thread.Sleep方法调用。这将导致线程执行该代码时,在打印任何数字之前会等待指定的时间(本例中是2秒钟)。当线程处于休眠状态时,它会占用尽可能少的CPU时间。结果我们会发现通常后运行的PrintNumbers方法中的代码会比独立线程中的PrintNumbersWithDelay方法中的代码先执行。1.4 线程等待

本节将展示如何让程序等待另一个线程中的计算完成,然后在代码中使用该线程的计算结果。使用Thread.Sleep行不通,因为并不知道执行计算需要花费的具体时间。1.4.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe3目录中。1.4.2 实现方式

请执行以下步骤来了解如何让一个程序等待另一个线程中的计算完成,并随后使用该线程的计算结果:

1.启动Visual Studio 2015。创建一个新的C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.4.3 工作原理

当程序运行时,启动了一个耗时较长的线程来打印数字,打印每个数字前要等待两秒。但我们在主程序中调用了t.Join方法,该方法允许我们等待直到线程t完成。当线程t完成时,主程序会继续运行。借助该技术可以实现在两个线程间同步执行步骤。第一个线程会等待另一个线程完成后再继续执行。第一个线程等待时是处于阻塞状态(正如1.3节中调用Thread.Sleep方法一样)。1.5 终止线程

本节将讲述如何终止线程的执行。1.5.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe4目录中。1.5.2 实现方式

请执行以下步骤来了解如何终止线程的执行:

1.启动Visual Studio 2015。创建一个新的C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.5.3 工作原理

当主程序和单独的数字打印线程运行时,我们等待6秒后对线程调用了t.Abort方法。这给线程注入了ThreadAbortException方法,导致线程被终结。这非常危险,因为该异常可以在任何时刻发生并可能彻底摧毁应用程序。另外,使用该技术也不一定总能终止线程。目标线程可以通过处理该异常并调用Thread.ResetAbort方法来拒绝被终止。因此并不推荐使用Abort方法来关闭线程。可优先使用一些其他方法,比如提供一个CancellationToken方法来取消线程的执行。在第3章中我们会讨论该方法。1.6 检测线程状态

本节将描述一个线程可能会有哪些状态。获取线程是否已经启动或是否处于阻塞状态等相应信息是非常有用的。请注意由于线程是独立运行的,所以其状态可以在任何时候被改变。1.6.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe5目录中。1.6.2 实现方式

请执行以下步骤来了解如何确定线程状态及获取线程相关的信息。

1.启动Visual Studio 2015。创建一个新的C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.6.3 工作原理

当主程序启动时定义了两个不同的线程。一个将被终止,另一个则会成功完成运行。线程状态位于Thread对象的ThreadState属性中。ThreadState属性是一个C#枚举对象。刚开始线程状态为ThreadState.Unstarted。然后我们启动线程,并估计在一个周期为30次迭代的区间中,线程状态会从ThreadState.Running变为ThreadState.WaitSleepJoin。请注意始终可以通过Thread.CurrentThread静态属性获得当前Thread对象。

如果实际情况与以上不符,请增加迭代次数。终止第一个线程后,会看到现在该线程状态为ThreadState.Aborted。程序也有可能会打印出ThreadState.AbortRequested状态。这充分说明了同步两个线程的复杂性。请记住不要在程序中使用线程终止。我在这里使用它只是为了展示相应的线程状态。

最后可以看到第二个线程t2成功完成并且状态为ThreadState.Stopped。另外还有一些其他的线程状态,但是要么已经被弃用,要么没有我们实验过的几种状态有用。1.7 线程优先级

本节将描述线程优先级的几种不同的可能选项。线程优先级决定了该线程可占用多少CPU时间。1.7.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe6目录中。1.7.2 实现方式

请执行以下步骤来了解线程优先级的工作方式:

1.启动Visual Studio 2015。新建一个C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.7.3 工作原理

当主程序启动时定义了两个不同的线程。第一个线程优先级为ThreadPriority.Highest,即具有最高优先级。第二个线程优先级为ThreadPriority.Lowest,即具有最低优先级。我们先打印出主线程的优先级值,然后在所有可用的CPU核心上启动这两个线程。如果拥有一个以上的计算核心,将在两秒钟内得到初步结果。最高优先级的线程通常会计算更多的迭代,但是两个值应该很接近。然而,如果有其他程序占用了所有的CPU核心运行负载,结果则会截然不同。

为了模拟该情形,我们设置了ProcessorAffinity选项,让操作系统将所有的线程运行在单个CPU核心(第一个核心)上。现在结果完全不同,并且计算耗时将超过2秒钟。这是因为CPU核心大部分时间在运行高优先级的线程,只留给剩下的线程很少的时间来运行。

请注意这是操作系统使用线程优先级的一个演示。通常你无需使用这种行为编写程序。1.8 前台线程和后台线程

本节将描述前台线程和后台线程,及如何设置该选项来影响程序的行为。1.8.1 准备工作

为了学习本节,你需要安装Visual Studio 2015。除此之外无需其他准备。本节的源代码放置在BookSamples\Chapter1\Recipe7目录中。1.8.2 实现方式

请执行以下步骤来了解程序中前台线程和后台线程的效果:

1.启动Visual Studio 2015。新建一个C#控制台应用程序项目。

2.在Program.cs文件中加入以下using指令:

3.在Main方法下面加入以下代码片段:

4.在Main方法中加入以下代码片段:

5.运行程序。1.8.3 工作原理

当主程序启动时定义了两个不同的线程。默认情况下,显式创建的线程是前台线程。通过手动的设置threadTwo对象的IsBackground属性为true来创建一个后台线程。通过配置来实现第一个线程会比第二个线程先完成。然后运行程序。

第一个线程完成后,程序结束并且后台线程被终结。这是前台线程与后台线程的主要区别:进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作。

一个重要注意事项是如果程序定义了一个不会完成的前台线程,主程序并不会正常结束。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载