在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度、实现取消选项的相关知识。

三、线程池和并行度

  在这一小节中,我们将学习对于大量的异步操作,使用线程池和分别使用单独的线程在性能上有什么差异性。具体操作步骤如下:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

 using System;
using System.Diagnostics;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe03
{
class Program
{
static void UseThreads(int numberOfOperations)
{
using(var countdown=new CountdownEvent(numberOfOperations))
{
WriteLine("Scheduling work by creating threads");
for(int i = ; i < numberOfOperations; i++)
{
var thread = new Thread(() =>
{
Write($"{CurrentThread.ManagedThreadId},");
Sleep();
countdown.Signal();
});
thread.Start();
}
countdown.Wait();
WriteLine();
}
} static void UseThreadPool(int numberOfOperations)
{
using(var countdown=new CountdownEvent(numberOfOperations))
{
WriteLine("Starting work on a threadpool");
for(int i = ; i < numberOfOperations; i++)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Write($"{CurrentThread.ManagedThreadId},");
Sleep();
countdown.Signal();
});
}
countdown.Wait();
WriteLine();
}
} static void Main(string[] args)
{
const int numberOfOperations = ;
var sw = new Stopwatch();
sw.Start();
UseThreads(numberOfOperations);
sw.Stop();
WriteLine($"Execution time using threads: {sw.ElapsedMilliseconds}"); sw.Reset();
sw.Start();
UseThreadPool(numberOfOperations);
sw.Stop();
WriteLine($"Execution time using the thread pool: {sw.ElapsedMilliseconds}");
}
}
}

3、运行该控制台应用程序,运行效果(每次运行效果可能不同)如下图所示:

  在上述代码中,我们首先创建了500个线程来执行异步操作,我们发现使用每个单独的线程执行异步操作所消耗的时间为175毫秒。然后我们使用线程池来执行500个异步操作,我们发现所消耗的时间为9432毫秒。这说明使用线程池来执行大并发的异步操作会节省操作系统的内存和线程数,但是会严重影响应用程序的性能。

四、实现取消选项

  在这一小节中,我们将学习如何在线程池中取消一个异步操作。具体步骤如下所示:

1、使用Visual Studio 2015创建一个新的控制台应用程序。

2、双击打开“Program.cs”文件,编写代码如下所示:

 using System;
using System.Threading;
using static System.Console;
using static System.Threading.Thread; namespace Recipe04
{
class Program
{
// CancellationToken:传播有关应取消操作的通知。
static void AsyncOperation1(CancellationToken token)
{
WriteLine("Starting the first task");
for (int i = ; i < ; i++)
{
// IsCancellationRequested:获取是否已请求取消此标记。
// 如果已请求取消此标记,则为 true,否则为 false。
if (token.IsCancellationRequested)
{
WriteLine("The first task has been canceled.");
return;
}
Sleep(TimeSpan.FromSeconds());
}
WriteLine("The first task has completed succesfully");
} static void AsyncOperation2(CancellationToken token)
{
try
{
WriteLine("Starting the second task");
for (int i = ; i < ; i++)
{
// 如果已请求取消此标记,则引发 System.OperationCanceledException。
token.ThrowIfCancellationRequested();
Sleep(TimeSpan.FromSeconds());
}
WriteLine("The second task has completed succesfully");
}
catch (OperationCanceledException)
{
WriteLine("The second task has been canceled.");
}
} static void AsyncOperation3(CancellationToken token)
{
bool cancellationFlag = false;
// 注册一个将在取消此 System.Threading.CancellationToken 时调用的委托。
// Register的参数是一个Action类型的委托,该委托在取消 System.Threading.CancellationToken 时执行
token.Register(() => cancellationFlag = true);
WriteLine("Starting the third task");
for (int i = ; i < ; i++)
{
if (cancellationFlag)
{
WriteLine("The third task has been canceled.");
return;
}
Sleep(TimeSpan.FromSeconds());
}
WriteLine("The third task has completed succesfully");
} static void Main(string[] args)
{
// CancellationTokenSource:通知 System.Threading.CancellationToken,告知其应被取消。
using (var cts = new CancellationTokenSource())
{
// 获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken。
CancellationToken token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation1(token));
Sleep(TimeSpan.FromSeconds());
// 传达取消请求。
cts.Cancel();
} // CancellationTokenSource:通知 System.Threading.CancellationToken,告知其应被取消。
using (var cts = new CancellationTokenSource())
{
// 获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken。
CancellationToken token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation2(token));
Sleep(TimeSpan.FromSeconds());
// 传达取消请求。
cts.Cancel();
} // CancellationTokenSource:通知 System.Threading.CancellationToken,告知其应被取消。
using (var cts = new CancellationTokenSource())
{
// 获取与此 System.Threading.CancellationTokenSource 关联的 System.Threading.CancellationToken。
CancellationToken token = cts.Token;
ThreadPool.QueueUserWorkItem(_ => AsyncOperation3(token));
Sleep(TimeSpan.FromSeconds());
// 传达取消请求。
cts.Cancel();
} Sleep(TimeSpan.FromSeconds());
}
}
}

3、运行该控制台应用程序,运行效果如下图所示:

  在上述代码中,我们使用了CancellationTokenSource和CancellationToken类,这两个类在.NET 4.0引入,现在已经成为取消异步操作事实上的标准。

  在“AsyncOperation1”方法中,我们仅仅是轮询检查“CancellationToken.IsCancellationRequested”属性,如果该属性为true,这意味着我们的操作已被取消,我们必须放弃此次操作。

  在“AsyncOperation2”方法中,我们调用CancellationToken的“ThrowIfCancellationRequested”方法来检查操作是否已被取消,如果已被取消,该方法会抛出OperationCanceledException异常,我们使用try/catch块捕获这个异常来中止异步操作的执行。

  在“AsyncOperation3”方法中,我们调用CancellationToken的“Register”方法来注册一个异步操作被取消时被调用的回调方法。这种方式可以允许我们将取消操作的逻辑链接到另一个异步操作中。

C#多线程之线程池篇2的更多相关文章

  1. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  2. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  3. JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor

    JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...

  4. Qt中的多线程与线程池浅析+实例

    1. Qt中的多线程与线程池 今天学习了Qt中的多线程和线程池,特写这篇博客来记录一下 2. 多线程 2.1 线程类 QThread Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一 ...

  5. Java 多线程:线程池

    Java 多线程:线程池 作者:Grey 原文地址: 博客园:Java 多线程:线程池 CSDN:Java 多线程:线程池 工作原理 线程池内部是通过队列结合线程实现的,当我们利用线程池执行任务时: ...

  6. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  7. C#多线程之线程同步篇2

    在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...

  8. 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法

    [源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...

  9. ExecutorService 建立一个多线程的线程池的步骤

    ExecutorService 建立一个多线程的线程池的步骤: 线程池的作用: 线程池功能是限制在系统中运行的线程数. 依据系统的环境情况,能够自己主动或手动设置线程数量.达到执行的最佳效果:少了浪费 ...

随机推荐

  1. 复杂的 Hash 函数组合有意义吗?

    很久以前看到一篇文章,讲某个大网站储存用户口令时,会经过十分复杂的处理.怎么个复杂记不得了,大概就是先 Hash,结果加上一些特殊字符再 Hash,结果再加上些字符.再倒序.再怎么怎么的.再 Hash ...

  2. rnandroid环境搭建

    react-native 环境搭建具体步骤这个大家已经玩烂了,这个主要是记录下来自己做win7系统遇到的坑 1.com.android.ddmlib.installexception 遇到这个问题,在 ...

  3. C# - 值类型、引用类型&走出误区,容易错误的说法

    1. 值类型与引用类型小总结 1)对于引用类型的表达式(如一个变量),它的值是一个引用,而非对象. 2)引用就像URL,是允许你访问真实信息的一小片数据. 3)对于值类型的表达式,它的值是实际的数据. ...

  4. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  5. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

  6. 记一次.NET代码重构

    好久没写代码了,终于好不容易接到了开发任务,一看时间还挺充足的,我就慢慢整吧,若是遇上赶进度,基本上直接是功能优先,完全不考虑设计.你可以认为我完全没有追求,当身后有鞭子使劲赶的时候,神马设计都是浮云 ...

  7. 纯javaScript、jQuery实现个性化图片轮播

    纯javaScript实现个性化图片轮播 轮播原理说明<如上图所示>: 1. 画布部分(可视区域)属性说明:overflow:hidden使得超出画布部分隐藏或说不可见.position: ...

  8. Lucene4.4.0 开发之排序

    排序是对于全文检索来言是一个必不可少的功能,在实际运用中,排序功能能在某些时候给我们带来很大的方便,比如在淘宝,京东等一些电商网站我们可能通过排序来快速找到价格最便宜的商品,或者通过排序来找到评论数最 ...

  9. Princeton Algorithms week3 Assignment

    这周编程作业是实现检测点共线的算法.和排序算法有关系的地方在于,对斜率排序后可以很快的检测出来哪些点是共线的,另外这个算法的瓶颈也在于排序的性能. 一点收获: java传参数时传递的是值,这很多人都知 ...

  10. 集成基于CAS协议的单点登陆

    相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...