多线程编制程序学习笔记,八线程之线程池篇3

目录

接上文
四线程编制程序学习笔记——线程池

C#十二线程之线程池篇3,

  在上一篇C#十二线程之线程池篇第22中学,大家第三学习了线程池和并行度以及怎样促成撤废选项的相关文化。在这一篇中,大家器重学习如何运用等待句柄和过期、使用计时器和利用BackgroundWorker组件的连带知识。

伍 、使用等待句柄和过期

  在这一小节中,大家将学习怎样在线程池中落到实处超时和科学地促成等待。具体操作步骤如下:

多线程编制程序学习笔记,八线程之线程池篇3。一 、使用Visual Studio 二零一四创办贰个新的控制台应用程序。

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

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe05
 7 {
 8     class Program
 9     {
10         // CancellationTokenSource:通知System.Threading.CancellationToken,告知其应被取消。
11         static void WorkerOperationWait(CancellationTokenSource cts, bool isTimedOut)
12         {
13             if (isTimedOut)
14             {
15                 // 传达取消请求。
16                 cts.Cancel();
17                 WriteLine("Worker operation timed out and was canceled.");
18             }
19             else
20             {
21                 WriteLine("Worker operation succeeded.");
22             }
23         }
24 
25         // CancellationToken:传播有关应取消操作的通知。
26         // ManualResetEvent:通知一个或多个正在等待的线程已发生事件。
27         static void WorkerOperation(CancellationToken token, ManualResetEvent evt)
28         {
29             for (int i = 0; i < 6; i++)
30             {
31                 // 获取是否已请求取消此标记。如果已请求取消此标记,则为 true;否则为 false。
32                 if (token.IsCancellationRequested)
33                 {
34                     return;
35                 }
36                 Sleep(TimeSpan.FromSeconds(1));
37             }
38             // 将事件状态设置为终止状态,允许一个或多个等待线程继续。
39             evt.Set();
40         }
41 
42         static void RunOperations(TimeSpan workerOperationTimeout)
43         {
44             using (var evt = new ManualResetEvent(false))
45             using (var cts = new CancellationTokenSource())
46             {
47                 // 注册一个等待System.Threading.WaitHandle的委托,并指定一个System.TimeSpan值来表示超时时间。
48                 // 第一个参数:要注册的System.Threading.WaitHandle。使用System.Threading.WaitHandle而非 System.Threading.Mutex。
49                 // 第二个参数:waitObject参数终止时调用的System.Threading.WaitOrTimerCallback 委托。
50                 // 第三个参数:传递给委托的对象。
51                 // 第四个参数:System.TimeSpan表示的超时时间。如果timeout为0(零),则函数将测试对象的状态并立即返回。如果timeout为 -1,则函数的超时间隔永远不过期。
52                 // 第五个参数:如果为true,表示在调用了委托后,线程将不再在waitObject参数上等待;如果为false,表示每次完成等待操作后都重置计时器,直到注销等待。
53                 // 返回值:封装本机句柄的System.Threading.RegisteredWaitHandle。
54                 var worker = ThreadPool.RegisterWaitForSingleObject(evt, (state, isTimedOut) => WorkerOperationWait(cts, isTimedOut), null, workerOperationTimeout, true);
55 
56                 WriteLine("Starting long running operation...");
57                 // ThreadPool.QueueUserWorkItem:将方法排入队列以便执行。此方法在有线程池线程变得可用时执行。
58                 // cts.Token:获取与此System.Threading.CancellationTokenSource关联的System.Threading.CancellationToken。
59                 ThreadPool.QueueUserWorkItem(_ => WorkerOperation(cts.Token, evt));
60 
61                 Sleep(workerOperationTimeout.Add(TimeSpan.FromSeconds(2)));
62 
63                 // 取消由System.Threading.ThreadPool.RegisterWaitForSingleObject方法发出的已注册等待操作。
64                 worker.Unregister(evt);
65             }
66         }
67 
68         static void Main(string[] args)
69         {
70             // 实现超时
71             RunOperations(TimeSpan.FromSeconds(5));
72             // 实现等待
73             RunOperations(TimeSpan.FromSeconds(7));
74         }
75     }
76 }

③ 、运维该控制台应用程序,运转作效果果如下图所示:

美高梅开户网址 1

  线程池还有另二个使得的点子:ThreadPool.RegisterWaitForSingleObject,该方法允许我们将回调方法放入线程池的连串中,当所提供的等候句柄发送信号只怕逾期发生时,该回调方法即被实施。那允许大家对线程池中的操作达成超时。

  在第八1行代码处,大家在主线程中调用了“RunOperations”方法,并给它的workerOperationTimeout参数字传送递了数值5,表示超时时间为5秒。

  在第④4行代码处,我们调用了ThreadPool的“RegisterWaitForSingleObject”静态方法,并钦命了回调方法所要执行的操作是“WorkerOperationWait”方法,超时时间是5秒。

  在第69行代码处,大家调用ThreadPool的“QueueUserWorkItem”静态方法来实施“WorkerOperation”方法,而该措施所成本的年月为6秒,在这六秒中内早已在线程池中发送了晚点,所以会履行第23~18行和第32~35行处的代码。

  在第103行代码处,大家传递了数值7给“RunOperations”方法,设置线程池的晚点时间为7秒,因为“WorkerOperation”方法的推行时间为6秒,所以在那种情景下并未产生超时,成功施行完成“WorkerOperation”方法。

 6、使用计时器

  在这一小节中,大家将学习怎么行使System.Threading.Timer对象在线程池中定期地调用1个异步操作。具体操作步骤如下所示:

① 、使用Visual Studio 二零一五开立2个新的控制台应用程序。

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

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe06
 7 {
 8     class Program
 9     {
10         static Timer timer;
11 
12         static void TimerOperation(DateTime start)
13         {
14             TimeSpan elapsed = DateTime.Now - start;
15             WriteLine($"{elapsed.Seconds} seconds from {start}. Timer thread pool thread id: {CurrentThread.ManagedThreadId}");
16         }
17 
18         static void Main(string[] args)
19         {
20             WriteLine("Press 'Enter' to stop the timer...");
21             DateTime start = DateTime.Now;
22             // 初始化Timer类的新实例,使用System.TimeSpan值来度量时间间隔。
23             // 第一个参数:一个System.Threading.TimerCallback委托,表示要执行的方法。
24             // 第二个参数:一个包含回调方法要使用的信息的对象,或者为null。
25             // 第三个参数:System.TimeSpan,表示在callback参数调用它的方法之前延迟的时间量。指定-1毫秒以防止启动计时器。指定零(0)可立即启动计时器。
26             // 第四个参数:在调用callback所引用的方法之间的时间间隔。指定-1毫秒可以禁用定期终止。
27             timer = new Timer(_ => TimerOperation(start), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2));
28             try
29             {
30                 Sleep(TimeSpan.FromSeconds(6));
31                 // 更改计时器的启动时间和方法调用之间的时间间隔,使用System.TimeSpan值度量时间间隔。
32                 // 第一个参数:一个System.TimeSpan,表示在调用构造System.Threading.Timer时指定的回调方法之前的延迟时间量。指定负-1毫秒以防止计时器重新启动。指定零(0)可立即重新启动计时器。
33                 // 第二个参数:在构造System.Threading.Timer时指定的回调方法调用之间的时间间隔。指定-1毫秒可以禁用定期终止。
34                 timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(4));
35                 ReadLine();
36             }
37             finally
38             {
39                 timer.Dispose();
40             }
41         }
42     }
43 }

③ 、运转该控制台应用程序,运维效果(每一回运转效果说不定分裂)如下图所示:

美高梅开户网址 2

  首先,大家成立了二个Timer实例,它的构造方法的率先个参数是3个lambda表明式,表示要在线程池中推行的代码,在该表明式中我们调用了“TimerOperation”方法,并给它提供了二个发端时间值。由于我们尚无应用state对象,因而大家给Timer的构造方法的第3个参数字传送递了null。第⑤个参数表示第②遍实施“TimerOperation”所要开销的年HTC1分钟。第7个参数表示每趟调用“TimerOperation”之间的年月距离为2分钟。

  在主线程阻塞6分钟之后,我们调用了Timer实例的“Change”方法,更改了每趟调用“TimerOperation”之间的年华间隔为4分钟。

  最终,大家静观其变输入“Enter”键来了却应用程序。

七、使用BackgroundWorker组件

   在这一小节中,咱们学习其它一种异步编制程序的法门:BackgroundWorker组件。在那些组件的扶助下,大家可以透过一层层事件和事件处理方法组织大家的异步代码。具体操作步骤如下所示:

① 、使用Visual Studio 二〇一五创设八个新的控制台应用程序。

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

  1 using System;
  2 using System.ComponentModel;
  3 using System.Threading;
  4 using static System.Console;
  5 using static System.Threading.Thread;
  6 
  7 namespace Recipe07
  8 {
  9     class Program
 10     {
 11         static void WorkerDoWork(object sender, DoWorkEventArgs e)
 12         {
 13             WriteLine($"DoWork thread pool thread id: {CurrentThread.ManagedThreadId}");
 14             var bw = (BackgroundWorker)sender;
 15             for (int i = 1; i <= 100; i++)
 16             {
 17                 // 获取一个值,指示应用程序是否已请求取消后台操作。
 18                 // 如果应用程序已请求取消后台操作,则为 true;否则为 false。默认值为 false。
 19                 if (bw.CancellationPending)
 20                 {
 21                     e.Cancel = true;
 22                     return;
 23                 }
 24 
 25                 if (i % 10 == 0)
 26                 {
 27                     // 引发 System.ComponentModel.BackgroundWorker.ProgressChanged 事件。
 28                     // 参数:已完成的后台操作所占的百分比,范围从 0% 到 100%。
 29                     bw.ReportProgress(i);
 30                 }
 31 
 32                 Sleep(TimeSpan.FromSeconds(0.1));
 33             }
 34 
 35             // 获取或设置表示异步操作结果的值。
 36             e.Result = 42;
 37         }
 38 
 39         static void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
 40         {
 41             // e.ProgressPercentage:获取异步任务的进度百分比。
 42             WriteLine($"{e.ProgressPercentage}% completed. Progress thread pool thread id: {CurrentThread.ManagedThreadId}");
 43         }
 44 
 45         static void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
 46         {
 47             WriteLine($"Completed thread pool thread id: {CurrentThread.ManagedThreadId}");
 48             // e.Error:获取一个值,该值指示异步操作期间发生的错误。
 49             if (e.Error != null)
 50             {
 51                 // 打印出异步操作期间发生的错误信息。
 52                 WriteLine($"Exception {e.Error.Message} has occured.");
 53             }
 54             else if (e.Cancelled) // 获取一个值,该值指示异步操作是否已被取消。
 55             {
 56                 WriteLine($"Operation has been canceled.");
 57             }
 58             else
 59             {
 60                 // e.Result:获取表示异步操作结果的值。
 61                 WriteLine($"The answer is: {e.Result}");
 62             }
 63         }
 64 
 65         static void Main(string[] args)
 66         {
 67             // 初始化System.ComponentModel.BackgroundWorker类的新实例。该类在单独的线程上执行操作。
 68             var bw = new BackgroundWorker();
 69             // 获取或设置一个值,该值指示System.ComponentModel.BackgroundWorker能否报告进度更新。
 70             // 如果System.ComponentModel.BackgroundWorker支持进度更新,则为true;否则为false。默认值为false。
 71             bw.WorkerReportsProgress = true;
 72             // 获取或设置一个值,该值指示System.ComponentModel.BackgroundWorker是否支持异步取消。
 73             // 如果System.ComponentModel.BackgroundWorker支持取消,则为true;否则为false。默认值为false。
 74             bw.WorkerSupportsCancellation = true;
 75 
 76             // 调用System.ComponentModel.BackgroundWorker.RunWorkerAsync时发生。
 77             bw.DoWork += WorkerDoWork;
 78             // 调用System.ComponentModel.BackgroundWorker.ReportProgress(System.Int32)时发生。
 79             bw.ProgressChanged += WorkerProgressChanged;
 80             // 当后台操作已完成、被取消或引发异常时发生。
 81             bw.RunWorkerCompleted += WorkerCompleted;
 82 
 83             // 开始执行后台操作。
 84             bw.RunWorkerAsync();
 85 
 86             WriteLine("Press C to cancel work");
 87 
 88             do
 89             {
 90                 // 获取用户按下的下一个字符或功能键。按下的键可以选择显示在控制台窗口中。
 91                 // 确定是否在控制台窗口中显示按下的键。如果为 true,则不显示按下的键;否则为 false。
 92                 if (ReadKey(true).KeyChar == 'C')
 93                 {
 94                     // 请求取消挂起的后台操作。
 95                     bw.CancelAsync();
 96                 }
 97             }
 98             // 获取一个值,指示System.ComponentModel.BackgroundWorker是否正在运行异步操作。
 99             // 如果System.ComponentModel.BackgroundWorker正在运行异步操作,则为true;否则为false。
100             while (bw.IsBusy);
101         }
102     }
103 }

③ 、运营该控制台应用程序,运转效果(每回运维效果兴许区别)如下图所示:

美高梅开户网址 3

   在第⑥8行代码处,我们创设了三个BackgroundWorker组件的实例,并且在第⑦1行代码和第九4行代码处显著地注脚该实例帮忙进度更新和异步撤废操作。

  在第七7行代码、第柒9行代码和第十1行代码处,我们给该实例挂载了四个事件处理方法。每当DoWork、ProgressChanged和RunWorkerCompleted事件时有爆发时,都会执行相应的“WorkerDoWork方法”、“WorkerProgressChanged”方法和“WorkerCompleted”方法。

  别的代码请参考注释。

  源码下载

在上一篇C#四线程之线程池篇2中,大家根本学习了线程池和并行度以及如何兑现撤除选项的相干文化。在这一篇中,…

3.1 简介

  • 1.1
    简介
  • 1.2
    在线程池中调用委托
  • 1.3
    向线程池中放入异步操作
  • 1.4
    线程池与并行度
  • 1.5
    达成多个撤除选项
  • 1.6
    在线程池中运用等待事件处理器及超时
  • 1.7
    使用计时器
  • 1.8
    使用BackgroundWorker组件
  • 参照书籍
  • 笔者水平有限,若是不当欢迎各位批评指正!

接上文 四线程编制程序学习笔记——线程池

线程池首要用在急需大批量短距离赛跑的花费大的资源的景色。大家先行分配一些能源在线程池当中,当大家供给选用的时候,直接从池中取出,代替了重复创立,不用时候就送重返池个中。


伍 、在线程池中运用等待事件处理器与超时

.NET个中的线程池是受CLOdyssey来治本的。


本示例主要学习假如对线程池中的操作达成超时,并在线程池中正确等待。

.NET线程池有一个QueueUserWorkItem()的静态方法,这么些法子接收三个信托,每当该措施被调用后,委托进入内部的种类中,要是线程池个中没有任何线程,此时创立八个新的做事线程,并将队列的率先个委托放入到工作线程其中。

1.1 简介

在本章中,首要介绍线程池(ThreadPool)的使用;在C#中它叫System.Threading.ThreadPool,在使用线程池从前率先大家得了然三个题材,那正是怎么要使用线程池。其主要性缘由是创设2个线程的代价是昂贵的,成立二个线程会消耗过多的系统财富。

那么线程池是什么样缓解那几个题材的呢?线程池在初始时会自动成立一定量的线程供程序调用,使用时,开发职员并不直接分配线程,而是将索要做的干活放入线程池工作行列中,由线程池分配已部分线程实行拍卖,等处理实现后线程不是被销毁,而是重复回来线程池中,那样节约了创设线程的支付。

不过在使用线程池时,须要专注以下几点,那将相当主要。

  • 线程池不符合处理长日子运作的作业,可能处理须求与别的线程同步的学业。
  • 幸免将线程池中的工作线程分配给I/O首先的职务,那种职分应该利用TPL模型。
  • 如非必须,不要手动设置线程池的最小线程数和最大线程数,CL奇骏会自动的实行线程池的恢宏和收缩,手动干预往往让品质更差。

线程池还有二个ThreadPool.RegisterWaitForSingleObject,这几个法子允许大家将回调函数放入线程池中的队列中。当提供的等候事件处理器接收到信号或发生超时时,这几个回调函数将被调用,那样就完结了为线程池中操作完毕超时操作。

 
  美高梅开户网址 4

1.2 在线程池中调用委托

本节显示的是怎么着在线程池中怎么着异步的施行委托,然后将介绍1个叫异步编程模型(Asynchronous
Programming Model,简称APM)
的异步编制程序形式。

在本节及其后,为了下降代码量,在引用程序集注明地方暗中同意添加了using static System.Consoleusing static System.Threading.Thead注解,这样注明能够让我们在程序中少些一些意义相当小的调用语句。

示范代码如下所示,使用了家常成立线程和APM方式来推行同贰个职务。

static void Main(string[] args)
{
    int threadId = 0;

    RunOnThreadPool poolDelegate = Test;

    var t = new Thread(() => Test(out threadId));
    t.Start();
    t.Join();

    WriteLine($"手动创建线程 Id: {threadId}");

    // 使用APM方式 进行异步调用  异步调用会使用线程池中的线程
    IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "委托异步调用");
    r.AsyncWaitHandle.WaitOne();

    // 获取异步调用结果
    string result = poolDelegate.EndInvoke(out threadId, r);

    WriteLine($"Thread - 线程池工作线程Id: {threadId}");
    WriteLine(result);

    Console.ReadLine();
}

// 创建带一个参数的委托类型
private delegate string RunOnThreadPool(out int threadId);

private static void Callback(IAsyncResult ar)
{
    WriteLine("Callback - 开始运行Callback...");
    WriteLine($"Callback - 回调传递状态: {ar.AsyncState}");
    WriteLine($"Callback - 是否为线程池线程: {CurrentThread.IsThreadPoolThread}");
    WriteLine($"Callback - 线程池工作线程Id: {CurrentThread.ManagedThreadId}");
}

private static string Test(out int threadId)
{
    string isThreadPoolThread = CurrentThread.IsThreadPoolThread ? "ThreadPool - ": "Thread - ";

    WriteLine($"{isThreadPoolThread}开始运行...");
    WriteLine($"{isThreadPoolThread}是否为线程池线程: {CurrentThread.IsThreadPoolThread}");
    Sleep(TimeSpan.FromSeconds(2));
    threadId = CurrentThread.ManagedThreadId;
    return $"{isThreadPoolThread}线程池工作线程Id: {threadId}";
}

运转结果如下图所示,当中以Thread初始的为手动创制的线程输出的音信,而TheadPool为伊始线程池职务输出的音讯,Callback为APM方式运营职责达成后,执行的回调方法,能够清楚的观看,Callback的线程也是线程池的工作线程。

美高梅开户网址 5

在上文中,使用BeginOperationName/EndOperationName方法和.Net中的IAsyncResult目的的主意被喻为异步编制程序模型(或APM情势),那样的章程被叫做异步方法。使用委托的BeginInvoke方法来运营该信托,BeginInvoke接到二个回调函数,该回调函数会在职分处理完了后背调用,并且能够传递3个用户自定义的境况给回调函数。

至今那种APM编制程序方式用的越来越少了,更推荐使用任务并行库(Task Parallel
Library,简称TPL)
来公司异步API。

1.代码之类:

 

1.3 向线程池中放入异步操作

本节将介绍怎样将异步操作放入线程池中实施,并且怎么样传递参数给线程池中的线程。本节中最首要使用的是ThreadPool.QueueUserWorkItem()艺术,该措施可将急需周转的职责通过信托的花样传递给线程池中的线程,并且同意传递参数。

使用相比较不难,演示代码如下所示。演示了线程池使用中怎样传递方式和参数,最后索要小心的是行使了Lambda表明式和它的闭包机制。

static void Main(string[] args)
{
    const int x = 1;
    const int y = 2;
    const string lambdaState = "lambda state 2";

    // 直接将方法传递给线程池
    ThreadPool.QueueUserWorkItem(AsyncOperation);
    Sleep(TimeSpan.FromSeconds(1));

    // 直接将方法传递给线程池 并且 通过state传递参数
    ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");
    Sleep(TimeSpan.FromSeconds(1));

    // 使用Lambda表达式将任务传递给线程池 并且通过 state传递参数
    ThreadPool.QueueUserWorkItem(state =>
    {
        WriteLine($"Operation state: {state}");
        WriteLine($"工作线程 id: {CurrentThread.ManagedThreadId}");
        Sleep(TimeSpan.FromSeconds(2));
    }, "lambda state");

    // 使用Lambda表达式将任务传递给线程池 通过 **闭包** 机制传递参数
    ThreadPool.QueueUserWorkItem(_ =>
    {
        WriteLine($"Operation state: {x + y}, {lambdaState}");
        WriteLine($"工作线程 id: {CurrentThread.ManagedThreadId}");
        Sleep(TimeSpan.FromSeconds(2));
    }, "lambda state");

    ReadLine();
}

private static void AsyncOperation(object state)
{
    WriteLine($"Operation state: {state ?? "(null)"}");
    WriteLine($"工作线程 id: {CurrentThread.ManagedThreadId}");
    Sleep(TimeSpan.FromSeconds(2));
}

运作结果如下图所示。

美高梅开户网址 6

using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading;namespace ThreadTPLDemo{    class Program    {           static void Main(string[] args)        {            Console.WriteLine("开始测试线程池中定时运行操作。。。");            TimesOperation(TimeSpan.FromSeconds(5));//提供了5秒的操作时间,会超时            TimesOperation(TimeSpan.FromSeconds(9));//提供了9秒的操作时间,正常工作            Console.WriteLine("。。。。。。。。。。。。。。。。");            Console.Read();        }        private static void  TimesOperation(TimeSpan  workTimes)        {            using (var manuEvt=new ManualResetEvent(false))            {                using (var cts=new CancellationTokenSource                {                    Console.WriteLine("开始--线程池中的定时操作。。。");                    var work = ThreadPool.RegisterWaitForSingleObject                        (manuEvt, (state, isTimeOut) => AsyncOperWait(cts, isTimeOut), null, workTimes, true);                    Console.WriteLine("一个长时间运行的线程操作。。。");                    ThreadPool.QueueUserWorkItem(_ => AsyncOper(cts.Token, manuEvt));                    Console.WriteLine("。。。间隔2秒再次运行。。。");                    Thread.Sleep(workTimes.Add(TimeSpan.FromSeconds(2)));                    work.Unregister;                }            }         }        private static void AsyncOper(CancellationToken token,ManualResetEvent mrevt)        {            Console.WriteLine("开始--线程池中的第一个工作线程。。。");            for (int i = 0; i < 7; i++)            {                if (token.IsCancellationRequested)//判断是否已经取消操作                {                    Console.WriteLine("使用轮询方法取消工作线程  ID:{0}", Thread.CurrentThread.ManagedThreadId);                    return;                }                Thread.Sleep(TimeSpan.FromSeconds(1));            }            mrevt.Set();            Console.WriteLine("-------线程池中的第一个工作线程 发出信号----------");        }        private static void AsyncOperWait(CancellationTokenSource cts, bool isTimeOut)        {            Console.WriteLine("开始--线程池中的第二个工作线程。。。");            if (isTimeOut)//判断是否已经取消操作            {                cts.Cancel();                Console.WriteLine("工作线程已经超时,并取消。  ID:{0}", Thread.CurrentThread.ManagedThreadId);            }            else            {                Console.WriteLine("-------线程池中的第二个工作线程 工作完成----------");            }                        }    }} 

注意点:

1.4 线程池与并行度

在本节中,重若是利用普通创立线程和使用线程池内的线程在职责量比较大的情事下有啥分别,大家模拟了1个场景,成立了重重见仁见智的线程,然后分别使用普通成立线程格局和线程池形式看看有怎么样两样。

static void Main(string[] args)
{
    const int numberOfOperations = 500;
    var sw = new Stopwatch();
    sw.Start();
    UseThreads(numberOfOperations);
    sw.Stop();
    WriteLine($"使用线程执行总用时: {sw.ElapsedMilliseconds}");

    sw.Reset();
    sw.Start();
    UseThreadPool(numberOfOperations);
    sw.Stop();
    WriteLine($"使用线程池执行总用时: {sw.ElapsedMilliseconds}");

    Console.ReadLine();
}

static void UseThreads(int numberOfOperations)
{
    using (var countdown = new CountdownEvent(numberOfOperations))
    {
        WriteLine("通过创建线程调度工作");
        for (int i = 0; i < numberOfOperations; i++)
        {
            var thread = new Thread(() =>
            {
                Write($"{CurrentThread.ManagedThreadId},");
                Sleep(TimeSpan.FromSeconds(0.1));
                countdown.Signal();
            });
            thread.Start();
        }
        countdown.Wait();
        WriteLine();
    }
}

static void UseThreadPool(int numberOfOperations)
{
    using (var countdown = new CountdownEvent(numberOfOperations))
    {
        WriteLine("使用线程池开始工作");
        for (int i = 0; i < numberOfOperations; i++)
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                Write($"{CurrentThread.ManagedThreadId},");
                Sleep(TimeSpan.FromSeconds(0.1));
                countdown.Signal();
            });
        }
        countdown.Wait();
        WriteLine();
    }
}

实践结果如下,可知使用原来的成立线程执行,速度特别快。只花了2分钟,不过创造了500五个线程,而使用线程池相对来说相比慢,花了柒分钟,不过只创立了很少的线程,为操作系统节省了线程和内部存款和储蓄器空间,但花了越多的年月。

美高梅开户网址 7

2.主次结果如下。

①线程池内的操作,尽量放入长时间运作的工作

1.5 达成贰个撤回选项

在头里的稿子中有关联,如若必要结束七个线程的实施,那么能够行使Abort()艺术,可是有许多的原由并不引进应用Abort()方法。

此处推荐的措施是行使合营式废除(cooperative
cancellation)
,那是一种保证的技能来安全废除不再要求的职责。其关键运用CancellationTokenSourceCancellationToken七个类,具体用法见下边演示代码。

以下延时期码首倘使贯彻了运用CancellationTokenCancellationTokenSource来完结任务的撤销。不过职分废除后得以开始展览三种操作,分别是:直接重临、抛出ThrowIfCancellationRequesed丰裕和实践回调。详细请看代码。

static void Main(string[] args)
{
    // 使用CancellationToken来取消任务  取消任务直接返回
    using (var cts = new CancellationTokenSource())
    {
        CancellationToken token = cts.Token;
        ThreadPool.QueueUserWorkItem(_ => AsyncOperation1(token));
        Sleep(TimeSpan.FromSeconds(2));
        cts.Cancel();
    }

    // 取消任务 抛出 ThrowIfCancellationRequesed 异常
    using (var cts = new CancellationTokenSource())
    {
        CancellationToken token = cts.Token;
        ThreadPool.QueueUserWorkItem(_ => AsyncOperation2(token));
        Sleep(TimeSpan.FromSeconds(2));
        cts.Cancel();
    }

    // 取消任务 并 执行取消后的回调函数
    using (var cts = new CancellationTokenSource())
    {
        CancellationToken token = cts.Token;
        token.Register(() => { WriteLine("第三个任务被取消,执行回调函数。"); });
        ThreadPool.QueueUserWorkItem(_ => AsyncOperation3(token));
        Sleep(TimeSpan.FromSeconds(2));
        cts.Cancel();
    }

    ReadLine();
}

static void AsyncOperation1(CancellationToken token)
{
    WriteLine("启动第一个任务.");
    for (int i = 0; i < 5; i++)
    {
        if (token.IsCancellationRequested)
        {
            WriteLine("第一个任务被取消.");
            return;
        }
        Sleep(TimeSpan.FromSeconds(1));
    }
    WriteLine("第一个任务运行完成.");
}

static void AsyncOperation2(CancellationToken token)
{
    try
    {
        WriteLine("启动第二个任务.");

        for (int i = 0; i < 5; i++)
        {
            token.ThrowIfCancellationRequested();
            Sleep(TimeSpan.FromSeconds(1));
        }
        WriteLine("第二个任务运行完成.");
    }
    catch (OperationCanceledException)
    {
        WriteLine("第二个任务被取消.");
    }
}

static void AsyncOperation3(CancellationToken token)
{
    WriteLine("启动第三个任务.");
    for (int i = 0; i < 5; i++)
    {
        if (token.IsCancellationRequested)
        {
            WriteLine("第三个任务被取消.");
            return;
        }
        Sleep(TimeSpan.FromSeconds(1));
    }
    WriteLine("第三个任务运行完成.");
}

运作结果如下所示,符合预期结果。

美高梅开户网址 8

美高梅开户网址 9

②ASP.NET应用程序使用线程池不要把工作线程全体应用掉,否则web服务器将不能够处理新的伸手。

1.6 在线程池中央银行使等待事件处理器及逾期

本节将介绍怎么样在线程池中应用等待职责和什么进展过期处理,在那之中首要选取ThreadPool.RegisterWaitForSingleObject()办法,该措施允许传入三个WaitHandle指标,和内需实践的职务、超时时间等。通过利用那几个点子,可做到线程池意况下对过期职分的拍卖。

演示代码如下所示,运营了五次使用ThreadPool.RegisterWaitForSingleObject()编写超时期码的RunOperations()艺术,可是所盛传的逾期时间各异,所以导致二个势必超时和一个不会晚点的结果。

static void Main(string[] args)
{
    // 设置超时时间为 5s WorkerOperation会延时 6s 肯定会超时
    RunOperations(TimeSpan.FromSeconds(5));

    // 设置超时时间为 7s 不会超时
    RunOperations(TimeSpan.FromSeconds(7));
}

static void RunOperations(TimeSpan workerOperationTimeout)
{
    using (var evt = new ManualResetEvent(false))
    using (var cts = new CancellationTokenSource())
    {
        WriteLine("注册超时操作...");
        // 传入同步事件  超时处理函数  和 超时时间
        var worker = ThreadPool.RegisterWaitForSingleObject(evt
            , (state, isTimedOut) => WorkerOperationWait(cts, isTimedOut)
            , null
            , workerOperationTimeout
            , true);

        WriteLine("启动长时间运行操作...");
        ThreadPool.QueueUserWorkItem(_ => WorkerOperation(cts.Token, evt));

        Sleep(workerOperationTimeout.Add(TimeSpan.FromSeconds(2)));

        // 取消注册等待的操作
        worker.Unregister(evt);

        ReadLine();
    }
}

static void WorkerOperation(CancellationToken token, ManualResetEvent evt)
{
    for (int i = 0; i < 6; i++)
    {
        if (token.IsCancellationRequested)
        {
            return;
        }
        Sleep(TimeSpan.FromSeconds(1));
    }
    evt.Set();
}

static void WorkerOperationWait(CancellationTokenSource cts, bool isTimedOut)
{
    if (isTimedOut)
    {
        cts.Cancel();
        WriteLine("工作操作超时并被取消.");
    }
    else
    {
        WriteLine("工作操作成功.");
    }
}

运维结果如下图所示,与预期结果符合。

美高梅开户网址 10

次第运行现在按顺序放入了有的长日子运作的操作,这几个操作运营6秒,若是运维成功,则会安装一个马努alReset伊芙nt信号。若是撤消了那些操作,则那些操作会被舍弃。

ASP.NET只援引使用输入/输出密集型异步操作,因为其应用了3个例外的艺术,叫做I/O线程。

1.7 使用计时器

计时器是FCL提供的一个类,叫System.Threading.Timer,可要结果与创设周期性的异步操作。该类应用相比较不难。

以下的示范代码应用了定时器,并设置了定时器延时运转时间和周期时间。

static void Main(string[] args)
{
    WriteLine("按下回车键,结束定时器...");
    DateTime start = DateTime.Now;

    // 创建定时器
    _timer = new Timer(_ => TimerOperation(start), null
        , TimeSpan.FromSeconds(1)
        , TimeSpan.FromSeconds(2));
    try
    {
        Sleep(TimeSpan.FromSeconds(6));

        _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(4));

        ReadLine();
    }
    finally
    {
        //实现了IDispose接口  要及时释放
        _timer.Dispose();
    }
}

static Timer _timer;

static void TimerOperation(DateTime start)
{
    TimeSpan elapsed = DateTime.Now - start;
    WriteLine($"离 {start} 过去了 {elapsed.Seconds} 秒. " +
              $"定时器线程池 线程 id: {CurrentThread.ManagedThreadId}");
}

运维结果如下所示,可知定时器依据所设置的周期时间循环的调用TimerOperation()方法。

美高梅开户网址 11

笔者们还注册了第四个异步操作,当从马努alReset伊夫nt对象中经受了二个信号未来,那么些异步操作会被调用。假若第多个操作顺遂实行,则会设置信号。要是第1个操作实施超时,则会透过CancellationToken来打消第①个操作。

③线程池当中的线程全体是后台线程,由此要小心前台线程执行到位后,后台线程也将竣事工作。

1.8 使用BackgroundWorker组件

本节重中之重介绍BackgroundWorker零件的行使,该器件实际上被用于Windows窗体应用程序(Windows
Forms Application,简称
WPF)
中,通过它完毕的代码能够从来与UI控制器交互,特别自认和好用。

以身作则代码如下所示,使用BackgroundWorker来兑现对数码进行总结,并且让其辅助报告工作进程,帮助撤废任务。

static void Main(string[] args)
{
    var bw = new BackgroundWorker();
    // 设置可报告进度更新
    bw.WorkerReportsProgress = true;
    // 设置支持取消操作
    bw.WorkerSupportsCancellation = true;

    // 需要做的工作
    bw.DoWork += Worker_DoWork;
    // 工作处理进度
    bw.ProgressChanged += Worker_ProgressChanged;
    // 工作完成后处理函数
    bw.RunWorkerCompleted += Worker_Completed;

    bw.RunWorkerAsync();

    WriteLine("按下 `C` 键 取消工作");
    do
    {
        if (ReadKey(true).KeyChar == 'C')
        {
            bw.CancelAsync();
        }

    }
    while (bw.IsBusy);
}

static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    WriteLine($"DoWork 线程池 线程 id: {CurrentThread.ManagedThreadId}");
    var bw = (BackgroundWorker)sender;
    for (int i = 1; i <= 100; i++)
    {
        if (bw.CancellationPending)
        {
            e.Cancel = true;
            return;
        }
        if (i % 10 == 0)
        {
            bw.ReportProgress(i);
        }

        Sleep(TimeSpan.FromSeconds(0.1));
    }

    e.Result = 42;
}

static void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    WriteLine($"已完成{e.ProgressPercentage}%. " +
              $"处理线程 id: {CurrentThread.ManagedThreadId}");
}

static void Worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
    WriteLine($"完成线程池线程 id: {CurrentThread.ManagedThreadId}");
    if (e.Error != null)
    {
        WriteLine($"异常 {e.Error.Message} 发生.");
    }
    else if (e.Cancelled)
    {
        WriteLine($"操作已被取消.");
    }
    else
    {
        WriteLine($"答案是 : {e.Result}");
    }
}

运作结果如下所示。

美高梅开户网址 12

在本节中,使用了C#中的其余二个语法,叫事件(event)。当然那里的轩然大波分歧于从前在线程同步章节中涉嫌的事件,那里是观望者设计格局的体现,包括事件源、订阅者和事件处理程序。由此,除了异步APM情势意外,还有传闻事件的异步情势(伊芙nt-based
Asynchronous Pattern,简称 EAP)

注:当线程池中山高校量的操作被卡住时,上面的不二法门就万分管用了。

 

参考书籍

正文主要参考了以下几本书,在此对这么些笔者表示真心的多谢你们提供了那样好的材料。

  1. 《CLR via C#》
  2. 《C# in Depth Third Edition》
  3. 《Essential C# 6.0》
  4. 《Multithreading with C# Cookbook Second Edition》
  5. 《C#八线程编制程序实战》

美高梅开户网址 ,源码下载点击链接
以身作则源码下载

陆 、 使用计时器

3.2线程池中调用委托

作者水平有限,借使不当欢迎各位批评指正!

选择Threading.Timer对象达成线程池中的周期性调用的异步操作。

先是要掌握一个什么样是【异步编制程序模型(Asynchronous Programming Model简称APM)】

1.代码之类:

.NET 1.0 异步编制程序模型(APM),

using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading; namespace ThreadPoolDemo{      class Program    {        static Timer timer;          static void Main(string[] args)        {            Console.WriteLine("开始测试线程池中通过计时器运行操作,输入A停止计时器。。。");            DateTime startTime = DateTime.Now;            timer = new Timer(_=>TimesOperation(startTime),null,TimeSpan.FromSeconds(1),TimeSpan.FromSeconds(2));            Thread.Sleep(6000);            timer.Change(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4));             Console.WriteLine("。。。。。。。。。。。。。。。。");            ConsoleKeyInfo key = Console.ReadKey();            if (key.Key==ConsoleKey.A)            {                timer.Dispose();            }            Console.Read();        }        private static void TimesOperation(DateTime startTime)        {            TimeSpan time = DateTime.Now - startTime;            Console.WriteLine("线程 {0} 从 {1} 开始 运行了 {2} 秒",            Thread.CurrentThread.ManagedThreadId, startTime.ToString("yyyy-MM-dd HH:mm:ss"), time.Seconds);         }    }} 

.NET 2.0 基于事件的异步编制程序模型(EAP),

2.程序运维结果如下。

.NET 4.0 基于职务的异步编制程序模型(TAP)。

美高梅开户网址 13

本章首要精晓什么是APM和EAP。

程序运营时,首先创立了1个timer,首个参数是lambla表明式,将会在线程池中执行,第三个参数是null。然后调用TimerOperation方法,并给二个方始时间,并点名何时会首先次运维TimerOperation,以及今后的再一次调用间隔时间。

下边那篇文章介绍了异步编制程序模型,感觉挺好的,那里mark一下。

七、 使用BackgroundWorker组件

http://blog.csdn.net/xinke453/article/details/37810823

本示例使用BackgroundWorker组件达成异步操作。

组成笔者那本书上的德姆o,感觉通晓起来无鸭梨,哈哈~

程序运行时创立了四个BackgroundWorker对象实例,突显的指令那些后台工作线程帮助撤销操作及操作进程通报。

1     class Program

2     {

3         static void Main(string[] args)

4         {

5             int threadId = 0;

6

7             RunOnThreadPool poolDelegate = Test;

8                         //用创建线程的方法先创建了一个线程

9             var t = new Thread(() => Test(out threadId));

10             t.Start();

11             t.Join();

12

13             Console.WriteLine("Thread id: {0}", threadId);

14

15             /*

16              使用BeginInvoke来运行委托,Callback是一个回调函数,

17               "a delegate asynchronous call" 代表你希望转发给回调方法的一个对象的引用,

18              在回调方法中,可以查询IAsyncResult接口的AsyncState属性来访问该对象

19              */

20             IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");

21              //这个例子当中使用AsyncWaitHandle属性来等待直到操作完成★

22             r.AsyncWaitHandle.WaitOne();

23             //操作完成后,会得到一个结果,可以通过委托调用EndInvoke方法,将IAsyncResult对象传递给委托参数。

24             string result = poolDelegate.EndInvoke(out threadId, r);

25             

26             Console.WriteLine("Thread pool worker thread id: {0}", threadId);

27             Console.WriteLine(result);

28

29             Thread.Sleep(TimeSpan.FromSeconds(2));

30         }

31

32         private delegate string RunOnThreadPool(out int threadId);

33

34         private static void Callback(IAsyncResult ar)

35         {

36             Console.WriteLine("Starting a callback...");

37             Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);

38             Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);

39             Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);

40         }

41

42

43         private static string Test(out int threadId)

44         {

45             Console.WriteLine("Starting...");

46             Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);

47             Thread.Sleep(TimeSpan.FromSeconds(2));

48             threadId = Thread.CurrentThread.ManagedThreadId;

49             return string.Format("Thread pool worker thread id was: {0}", threadId);

1.代码之类:

美高梅开户网址 14

using System;using System.Collections.Generic;using System.ComponentModel;using System.Diagnostics;using System.Linq;using System.Text;using System.Threading;namespace ThreadTPLDemo{     class Program    {        static Timer timer;           static void Main(string[] args)        {            Console.WriteLine("开始测试 BackgroundWorker。。。");            BackgroundWorker bgwork = new BackgroundWorker();            bgwork.WorkerReportsProgress = true;            bgwork.WorkerSupportsCancellation = true;             bgwork.DoWork += worker_dowork;            bgwork.ProgressChanged += worker_ProgressChanged;            bgwork.RunWorkerCompleted += worker_Completed;             bgwork.RunWorkerAsync();//开始后台运行                    Console.WriteLine("。。。。。输入C取消BackgroundWorker后台组件。。。。。。。。。。。");            do            {                ConsoleKeyInfo key = Console.ReadKey();                if (key.KeyChar.ToString().ToUpper() == "C")                {                    bgwork.CancelAsync();                }            } while (bgwork.IsBusy);                   Console.Read();         }        static void worker_dowork(object sender,DoWorkEventArgs e)        {                       Console.WriteLine("线程 {0}  开始 运行",            Thread.CurrentThread.ManagedThreadId);            int result = 0;            var bgwork = (BackgroundWorker)sender;            for (int i = 0; i < 100; i++)            {                if (bgwork.CancellationPending)//已经取消后台操作                {                    e.Cancel = true;                    return;                }                if (i%10==0)                {                    bgwork.ReportProgress;//显示进度                }                Thread.Sleep(200);                result += i;            }            e.Result = result;        }        static void worker_ProgressChanged(object sender,ProgressChangedEventArgs e)        {            Console.WriteLine("线程 {0}   已经完成工作量的 {1} %",          Thread.CurrentThread.ManagedThreadId,e.ProgressPercentage);        }        static void worker_Completed(object sender,RunWorkerCompletedEventArgs e)        {            Console.WriteLine("线程 {0} 已经执行结束!",        Thread.CurrentThread.ManagedThreadId);            if (e.Error!=null)            {                Console.WriteLine("线程 {0} 发生错误,错误信息:{1}",     Thread.CurrentThread.ManagedThreadId,e.Error.Message);            }            else if (e.Cancelled)            {                Console.WriteLine("线程 {0} 已经取消",   Thread.CurrentThread.ManagedThreadId);            }            else            {                Console.WriteLine("线程 {0} 执行成功,结果是:{1}",   Thread.CurrentThread.ManagedThreadId, e.Result);            }        }    }}

 

2.先后平常化履行实现,如下图。

在意:在这些例子个中,主线程调用Thread.Sleep(TimeSpan.FromSeconds(2));若是没那句话,回调函数就不会被执行了,

美高梅开户网址 15

以为线程池是后台线程,此时主线程停止,那么后台线程也随后甘休了,所以恐怕不会进行。

  1. 程序执行的中途,人工干预,撤销。如下图。请看下图天青框的岗位,我输入了字母C,则线程被吊销。

对此访问异步操作的结果,APM提供了各类方法供开发职员选取:

美高梅开户网址 16

①在调用BeginXxx方法的线程上调用EndXxx方法来获取异步操作的结果,可是那种方法会堵塞调用线程,知道操作达成之后调用线程才继续运维。

在先后中大家定义了多少个事件。

②查询IAsyncResult的AsyncWaitHandle属性,从而取得WaitHandle,然后再调用它的WaitOne方法来使一个线程阻塞并听候操作完毕再调用EndXxx方法来赢得操作的结果。

  1. DoWork事件,当3个后台工作对象通过RunWorkerAsync运转三个异步操作时,将调用那一个事件处理器。这几个事件处理器会运营在线程池中,当运维甘休,将运维结果做为参数字传送递给RunWorkerCompleted事件,同时触发此事件。
  2. ProgressChanged事件,通过接收BackgroundWorker的ReportProgress方法传递过来的参数,彰显线程执行的进度。
  3. RunWorkerCompleted事件,在此事件中得以领略操作是成功实现,依然时有产生了不当,或是被打消。

(本例子当中使用了那些格局)

③循环查询IAsyncResult的IsComplete属性,操作完结后再调用EndXxx方法来获得操作重临的结果。

④用到 AsyncCallback委托来钦赐操作完毕时要调用的主意,在操作完毕后调用的法门中调用EndXxx操作来取得异步操作的结果。

★★★推荐使用第三种方法,因为那时不会阻塞执行BeginXxx方法的线程,可是其余三种都会卡住调用线程,也便是效果和选取同步方法是相同,个人感觉根本失去了异步编制程序的表征,所以任何二种办法得以简简单单询问下,在实际上异步编制程序中都是使用委托的艺术。

3.3向线程池中投入异步操作

QueueUserWorkItem方法的定义!

1     [SecuritySafeCritical]
2     public static bool QueueUserWorkItem(WaitCallback callBack);

1     [SecuritySafeCritical]
2     public static bool QueueUserWorkItem(WaitCallback callBack, object state);

实例:
1     class Program
2     {
3         static void Main(string[] args)
4         {
5             const int x = 1;
6             const int y = 2;
7             const string lambdaState = "lambda state 2";
8            //方法一,直接调用QueueUserWorkItem传入单个参数,作为回调函数
9            ThreadPool.QueueUserWorkItem(AsyncOperation);
10           Thread.Sleep(TimeSpan.FromSeconds(1));
11 
12           //方法二,传入回调函数以及状态参数
13           ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");
14           Thread.Sleep(TimeSpan.FromSeconds(1));
15 
16           //方法三,使用labmbda表达式
17           ThreadPool.QueueUserWorkItem( state => {
18                     Console.WriteLine("Operation state: {0}", state);
19                     Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
20                     Thread.Sleep(TimeSpan.FromSeconds(2));
21                 }, "lambda state");
22 
23           //方法四,使用闭包机制
24           ThreadPool.QueueUserWorkItem( _ =>
25             {
26                 Console.WriteLine("Operation state: {0}, {1}", x+y, lambdaState);
27                 Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
28                 Thread.Sleep(TimeSpan.FromSeconds(2));
29             }, "lambda state");
30 
31             Thread.Sleep(TimeSpan.FromSeconds(2));
32         }
33 
34         private static void AsyncOperation(object state)
35         {
36             Console.WriteLine("Operation state: {0}", state ?? "(null)");
37             Console.WriteLine("Worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
38             Thread.Sleep(TimeSpan.FromSeconds(2));
39         }

美高梅开户网址 17

扩展:C#闭包(Closure)机制是何等?

 

3.4线程池与并行度

上面那几个实例呈现线程池怎么样行事于大量异步操作,以及他和创制的大气单身线程格局的区分。

1     class Program
2     {
3         static void Main(string[] args)
4         {
5             const int numberOfOperations = 500;
6             var sw = new Stopwatch();
7             sw.Start();
8             UseThreads(numberOfOperations);
9             sw.Stop();
10             Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);
11 
12             sw.Reset();
13             sw.Start();
14             UseThreadPool(numberOfOperations);
15             sw.Stop();
16             Console.WriteLine("Execution time using threads: {0}", sw.ElapsedMilliseconds);
17         }
18 
19         static void UseThreads(int numberOfOperations)
20         {
21             using (var countdown = new CountdownEvent(numberOfOperations))
22             {
23                 Console.WriteLine("Scheduling work by creating threads");
24                 for (int i = 0; i < numberOfOperations; i++)
25                 {
26                     var thread = new Thread(() => {
27                         Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
28                         Thread.Sleep(TimeSpan.FromSeconds(0.1));
29                         countdown.Signal();
30                     });
31                     thread.Start();
32                 }
33                 countdown.Wait();
34                 Console.WriteLine();
35             }
36         }
37 
38         static void UseThreadPool(int numberOfOperations)
39         {
40             using (var countdown = new CountdownEvent(numberOfOperations))
41             {
42                 Console.WriteLine("Starting work on a threadpool");
43                 for (int i = 0; i < numberOfOperations; i++)
44                 {
45                     ThreadPool.QueueUserWorkItem( _ => {
46                         Console.Write("{0},", Thread.CurrentThread.ManagedThreadId);
47                         Thread.Sleep(TimeSpan.FromSeconds(0.1));
48                         countdown.Signal();
49                     });
50                 }
51                 countdown.Wait();
52                 Console.WriteLine();
53             }
54         }
55     }

美高梅开户网址 18

美高梅开户网址 19

各自用创设大气线程的主意和线程池的主意履行500个Thread.Sleep(TimeSpan.FromSeconds(0.1))操作,

笔者们发现线程池费用了越来越多的时辰,不过占用的财富数量很少(通过ThreadId来看)。

 

3.5落到实处3个撤销选项

动用CancellationTokenSource和CancellationToken七个类来贯彻工作线程工作的吊销操作。

实例:
1     class Program
2     {
3         static void Main(string[] args)
4         {
5              using (var cts = new CancellationTokenSource())
6              {
7                   CancellationToken token = cts.Token;
8                   ThreadPool.QueueUserWorkItem(_ => AsyncOperation1(token));
9                   Thread.Sleep(TimeSpan.FromSeconds(2));
10                  cts.Cancel();
11             }
12 
13             using (var cts = new CancellationTokenSource())
14             {
15                  CancellationToken token = cts.Token;
16                  ThreadPool.QueueUserWorkItem(_ => AsyncOperation2(token));
17                  Thread.Sleep(TimeSpan.FromSeconds(2));
18                  cts.Cancel();
19              }
20 
21              using (var cts = new CancellationTokenSource())
22              {
23                   CancellationToken token = cts.Token;
24                   ThreadPool.QueueUserWorkItem(_ => AsyncOperation3(token));
25                   Thread.Sleep(TimeSpan.FromSeconds(2));
26                   cts.Cancel();
27              }
28 
29             Thread.Sleep(TimeSpan.FromSeconds(2));
30         }
31 
32        /// <summary>
33        /// 第一种采用轮询IsCancellationRequested属性的方式,如果为true,那么操作被取消
34        /// </summary>
35        /// <param name="token"></param>
36         static void AsyncOperation1(CancellationToken token)
37         {
38             Console.WriteLine("Starting the first task");
39             for (int i = 0; i < 5; i++)
40             {
41                 if (token.IsCancellationRequested)
42                 {
43                     Console.WriteLine("The first task has been canceled.");
44                     return;
45                 }
46                 Thread.Sleep(TimeSpan.FromSeconds(1));
47             }
48             Console.WriteLine("The first task has completed succesfully");
49         }
50        /// <summary>
51        /// 抛出一个OperationCancelledException异常
52        /// 这个允许操作之外控制取消过程,即需要取消操作的时候,通过操作之外的代码来处理
53        /// </summary>
54        /// <param name="token"></param>
55         static void AsyncOperation2(CancellationToken token)
56         {
57             try
58             {
59                 Console.WriteLine("Starting the second task");
60 
61                 for (int i = 0; i < 5; i++)
62                 {
63                     token.ThrowIfCancellationRequested();
64                     Thread.Sleep(TimeSpan.FromSeconds(1));
65                 }
66                 Console.WriteLine("The second task has completed succesfully");
67             }
68             catch (OperationCanceledException)
69             {
70                 Console.WriteLine("The second task has been canceled.");
71             }
72         }
73        /// <summary>
74        /// 第三种注册一个回调函数,当操作被取消时候,调用回调函数
75        /// </summary>
76        /// <param name="token"></param>
77         private static void AsyncOperation3(CancellationToken token)
78         {
79             bool cancellationFlag = false;
80             token.Register(() => cancellationFlag = true);
81             Console.WriteLine("Starting the third task");
82             for (int i = 0; i < 5; i++)
83             {
84                 if (cancellationFlag)
85                 {
86                     Console.WriteLine("The third task has been canceled.");
87                     return;
88                 }
89                 Thread.Sleep(TimeSpan.FromSeconds(1));
90             }
91             Console.WriteLine("The third task has completed succesfully");
92       

美高梅开户网址 20

CancellationTokenSource和CancellationToken多个类是.net4.0一会引入的,如今是贯彻异步操作裁撤事实标准。

 

3.6在线程池中应用等待事件处理器和过期

使用线程池个中的Threadpool.RegisterWaitSingleObject类来进行事件案处理。

RegisterWaitSingleObject的原型如下:

1  public static RegisteredWaitHandle RegisterWaitForSingleObject(
2           WaitHandle waitObject,
3           WaitOrTimerCallback callBack,
4           Object state,
5           int millisecondsTimeOutInterval,
6           bool executeOnlyOnce
7           )

参数

waitObject

要注册的 WaitHandle。使用 WaitHandle 而非 Mutex。

callBack

waitObject 参数终止时调用的 WaitOrTimerCallback 委托。

state

传送给委托的目标。

timeout

TimeSpan 表示的逾期时间。如若 timeout 为零,则函数测试目的的气象并当即重回。假使 timeout 为 -1,则函数的逾期间隔永远不超时。

executeOnlyOnce

一经为 true,表示在调用了寄托后,线程将不再在 waitObject 参数上等候;要是为 false,表示每回完成等待操作后都重置计时器,直到撤除等待。

 

返回值

封装本机句柄的 RegisteredWaitHandle。

深信看了这么些之后大家要么1头雾水,这么些方法的做用是向线程池添加1个得以定时执行的办法,第拾3个参数millisecondsTimeOutInterval 正是用来安装间隔执行的光阴,不过那里第四个参数executeOnlyOnce 会对第八个参数起功用,当它为true时,表示职务仅会履行3回,正是说它不会,像提姆er一样,每隔一定时间实施1遍,那么些效果的话用Timer控件也足以完结

该办法还在此基础上提供了依据信号量来触发执行职分。

信号量也叫开关量,故名思议,它唯有三种情状,不是true便是false,

WaitHandle正是那类开关量的底蕴类,继承它的类有Mutex,马努alReset伊芙nt,AutoReset伊芙nt,一般大家应用后两个

写法: 

        static ManualResetEvent wait2=new ManualResetEvent(false);

        static AutoResetEvent wait=new AutoResetEvent(false); 

我们得以在将其实例化时内定开关量的初步值。(true为有信号,false为没信号)

马努alReset伊芙nt和AutoReset伊芙nt的分别在于:

前端调用Set方法后将自动将开关量值将向来保持为true,后者调用Set方法将变成true随后马上变成false,可以将它通晓为1个脉冲。

例子
1     class Program
2     {
3         static void Main(string[] args)
4         {
5             //执行两次RunOperations操作,第一次会超时,第二次不会超时
6             RunOperations(TimeSpan.FromSeconds(5));
7             RunOperations(TimeSpan.FromSeconds(7));
8         }
9 
10         static void RunOperations(TimeSpan workerOperationTimeout)
11         {
12             //定义一个ManualResetEvent信号量,初始为false
13             using (var evt = new ManualResetEvent(false))
14             //实例化一个CancellationTokenSource实例,用于取消操作
15             using (var cts = new CancellationTokenSource())
16             {
17                 Console.WriteLine("Registering timeout operations...");
18                 //注册超时的被调用的回调函数。
19                 var worker = ThreadPool.RegisterWaitForSingleObject(
20                                         evt,
21                     (state, isTimedOut) => WorkerOperationWait(cts, isTimedOut), 
22                                         null, 
23                                         workerOperationTimeout, 
24                                         true );
25 
26                 Console.WriteLine("Starting long running operation...");
27                 //线程池执行WorkerOperation操作
28                 ThreadPool.QueueUserWorkItem(_ => WorkerOperation(cts.Token, evt));
29 
30                 Thread.Sleep(workerOperationTimeout.Add(TimeSpan.FromSeconds(2)));
31                 worker.Unregister(evt);
32             }
33         }
34 
35        /// <summary>
36        /// 线程池内需要被调用的操作
37        /// </summary>
38        /// <param name="token"></param>
39        /// <param name="evt"></param>
40         static void WorkerOperation(CancellationToken token, ManualResetEvent evt)
41         {
42             for(int i = 0; i < 6; i++)
43             {
44                 if (token.IsCancellationRequested)
45                 {
46                     return;
47                 }
48                 Thread.Sleep(TimeSpan.FromSeconds(1));
49             }
50             //设置信号量,此时evt为true。
51             evt.Set();
52         }
53 
54         /// <summary>
55         /// 超时时候执行的回调函数
56         /// </summary>
57         /// <param name="cts"></param>
58         /// <param name="isTimedOut"></param>
59         static void WorkerOperationWait(CancellationTokenSource cts, bool isTimedOut)
60         {
61             if (isTimedOut)
62             {
63                 cts.Cancel();
64                 Console.WriteLine("Worker operation timed out and was canceled.");
65             }
66             else
67             {
68                 Console.WriteLine("Worker operation succeded.");
69             }
70      

美高梅开户网址 21

 

3.7在线程池中央银行使计时器

采用system.Threading.Timer对象来在线程池中创建周期性调用的异步操作。

1     class Program
2     {
3         static void Main(string[] args)
4         {
5             Console.WriteLine("Press 'Enter' to stop the timer...");
6             DateTime start = DateTime.Now;
7             //实例化这个timer类
8             //一秒后执行TimerOperation这个操作,然后每隔2秒执行一次
9             _timer = new Timer(
10                                 _ => TimerOperation(start), 
11                                 null, 
12                                 TimeSpan.FromSeconds(1), 
13                                 TimeSpan.FromSeconds(2));
14 
15             Thread.Sleep(TimeSpan.FromSeconds(6));
16 
17             //改变计时器的运行时间,一秒后执行TimerOperation,然后每隔4秒执行一次
18             _timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(4));
19 
20             Console.ReadLine();
21 
22             _timer.Dispose();
23         }
24 
25         static Timer _timer;
26 
27         static void TimerOperation(DateTime start)
28         {
29             TimeSpan elapsed = DateTime.Now - start;
30             Console.WriteLine("{0} seconds from {1}. Timer thread pool thread id: {2}", elapsed.Seconds, start,
31             Thread.CurrentThread.ManagedThreadId);
32         }
33 

美高梅开户网址 22

 

3.8使用BackgroudWorker组件

本小节将介绍叁个异步编制程序格局的另一种方法,叫基于事件的异步形式(EAP)

先看多个例证吗:

1     class Program
2     {
3         static void Main(string[] args)
4         {
5               //实例化一个BackgroundWorker类
6               var bw = new BackgroundWorker();
7               //获取或设置一个值,该值指示 BackgroundWorker 能否报告进度更新。
8               bw.WorkerReportsProgress = true;
9               //设置后台工作线程是否支持取消操作
10             bw.WorkerSupportsCancellation = true;
11 
12             //给DoWork、ProgressChanged、RunWorkerCompleted事件绑定处理函数
13             bw.DoWork += Worker_DoWork;
14             bw.ProgressChanged += Worker_ProgressChanged;
15             bw.RunWorkerCompleted += Worker_Completed;
16 
17             //启动异步操作
18             bw.RunWorkerAsync();
19 
20             Console.WriteLine("Press C to cancel work");
21             do
22             {
23                 if (Console.ReadKey(true).KeyChar == 'C')
24                 {
25                     //取消操作
26                     bw.CancelAsync();
27                 }
28                 
29             }
30             while(bw.IsBusy);
31         }
32 
33         static void Worker_DoWork(object sender, DoWorkEventArgs e)
34         {
35             Console.WriteLine("DoWork thread pool thread id: {0}", Thread.CurrentThread.ManagedThreadId);
36             var bw = (BackgroundWorker) sender;
37             for (int i = 1; i <= 100; i++)
38             {
39 
40                 if (bw.CancellationPending)
41                 {
42                     e.Cancel = true;
43                     return;
44                 }
45 
46                 if (i%10 == 0)
47                 {
48                     bw.ReportProgress(i);
49                 }
50 
51                 Thread.Sleep(TimeSpan.FromSeconds(0.1));
52             }
53             e.Result = 42;
54         }
55 
56         static void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
57         {
58             Console.WriteLine("{0}% completed. Progress thread pool thread id: {1}", e.ProgressPercentage,
59                 Thread.CurrentThread.ManagedThreadId);
60         }
61 
62         static void Worker_Completed(object sender, RunWorkerCompletedEventArgs e)
63         {
64             Console.WriteLine("Completed threadpool thread id:{0}",Thread.CurrentThread.ManagedThreadId);
65             if (e.Error != null)
66             {
67                 Console.WriteLine("Exception {0} has occured.", e.Error.Message);
68             }
69             else if (e.Cancelled)
70             {
71                 Console.WriteLine("Operation has been canceled.");
72             }
73             else
74             {
75                 Console.WriteLine("The answer is: {0}", e.Result);
76             }
77         }

事件

 

名称

说明

 

Disposed

当通过调用 Dispose 方法释放组件时发生。(从 Component 继承。)

 

DoWork

调用 RunWorkerAsync 时发生。

RunWorkerAsync 方法提交一个启动以异步方式运行的操作的请求。发出该请求后,将引发 DoWork 事件,该事件随后开始执行后台操作。

如果后台操作已在运行,则再次调用 RunWorkerAsync 将引发 InvalidOperationException

 

ProgressChanged

调用 ReportProgress 时发生。

public void ReportProgress

(
    int percentProgress
)

percentProgress

已完成的后台操作所占的百分比,范围从 0% 到 100%。

如果您需要后台操作报告其进度,则可以调用 ReportProgress 方法来引发 ProgressChanged 事件。 WorkerReportsProgress 属性值必须是 true,否则 ReportProgress 将引发 InvalidOperationException

您需要实现一个有意义的方法,以便按照占已完成的总任务的百分比来度量后台操作的进度。

对 ReportProgress 方法的调用为异步且立即返回。The ProgressChanged 事件处理程序在创建 BackgroundWorker 的线程上执行。

 

RunWorkerCompleted

当后台操作已完成、被取消或引发异常时发生。

 

 

在该方法中可以知道操作是成功完成还是发生错误,亦或被取消。

貼り付け元  <https://msdn.microsoft.com/zh-cn/library/system.componentmodel.backgroundworker.aspx> 

水到渠成实现的时候

美高梅开户网址 23

职务废除的时候

美高梅开户网址 24

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图