C# 线程(Thread)
<div id="cnblogs_post_body" class="blogpost-body blogpost-body-html"><p><span style="font-size: 18px"><strong>一、基本概念</strong></span></p>
<p><span style="font-size: 18px"><strong>1、进程</strong></span></p>
<p><span style="font-size: 18px">首先打开任务管理器,查看当前运行的进程:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180530224252704-1834001918.png"></span></p>
<p><span style="font-size: 18px">从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢?</span></p>
<p><span style="font-size: 18px">进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。是应用程序的一个运行例程,是应用程序的一次动态执行过程。</span></p>
<p><span style="font-size: 18px"><strong>二、线程</strong></span></p>
<p><span style="font-size: 18px">在任务管理器里面查询当前总共运行的线程数:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180530224534305-772722615.png"></span></p>
<p><span style="font-size: 18px">线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。</span></p>
<p><span style="font-size: 18px"><strong>二、多线程</strong></span></p>
<p><span style="font-size: 18px">多线程的优点:可以同时完成多个任务;可以使程序的响应速度更快;可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给别的任务;可以随时停止任务;可以设置每个任务的优先级以优化程序性能。</span></p>
<p><span style="font-size: 18px">那么可能有人会问:为什么可以多线程执行呢?总结起来有下面两方面的原因:</span></p>
<p><span style="font-size: 18px">1、CPU运行速度太快,硬件处理速度跟不上,所以操作系统进行分时间片管理。这样,从宏观角度来说是多线程并发的,因为CPU速度太快,察觉不到,看起来是同一时刻执行了不同的操作。但是从微观角度来讲,同一时刻只能有一个线程在处理。</span></p>
<p><span style="font-size: 18px">2、目前电脑都是多核多CPU的,一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程。</span></p>
<p><span style="font-size: 18px">然而,多线程虽然有很多优点,但是也必须认识到多线程可能存在影响系统性能的不利方面,才能正确使用线程。不利方面主要有如下几点:</span></p>
<p><span style="font-size: 18px">(1)线程也是程序,所以线程需要占用内存,线程越多,占用内存也越多。</span></p>
<p><span style="font-size: 18px">(2)多线程需要协调和管理,所以需要占用CPU时间以便跟踪线程。</span></p>
<p><span style="font-size: 18px">(3)线程之间对共享资源的访问会相互影响,必须解决争用共享资源的问题。</span></p>
<p><span style="font-size: 18px">(4)线程太多会导致控制太复杂,最终可能造成很多程序缺陷。</span></p>
<p><span style="font-size: 18px">当启动一个可执行程序时,将创建一个主线程。在默认的情况下,C#程序具有一个线程,此线程执行程序中以Main方法开始和结束的代码,Main()方法直接或间接执行的每一个命令都有默认线程(主线程)执行,当Main()方法返回时此线程也将终止。</span></p>
<p><span style="font-size: 18px">一个进程可以创建一个或多个线程以执行与该进程关联的部分程序代码。在C#中,线程是使用Thread类处理的,该类在System.Threading命名空间中。使用Thread类创建线程时,只需要提供线程入口,线程入口告诉程序让这个线程做什么。通过实例化一个Thread类的对象就可以创建一个线程。创建新的Thread对象时,将创建新的托管线程。Thread类接收一个ThreadStart委托或ParameterizedThreadStart委托的构造函数,该委托包装了调用Start方法时由新线程调用的方法,示例代码如下:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> Thread thread=<span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span> ThreadStart(method));<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建线程</span>
<span style="color: rgba(0, 128, 128, 1)">2</span> thread.Start(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span></span></pre>
</div>
<p><span style="font-size: 18px">上面代码实例化了一个Thread对象,并指明将要调用的方法method(),然后启动线程。ThreadStart委托中作为参数的方法不需要参数,并且没有返回值。ParameterizedThreadStart委托一个对象作为参数,利用这个参数可以很方便地向线程传递参数,示例代码如下:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> Thread thread=<span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span> ParameterizedThreadStart(method));<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建线程</span>
<span style="color: rgba(0, 128, 128, 1)">2</span> thread.Start(<span style="color: rgba(128, 0, 128, 1)">3</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span></span></pre>
</div>
<p><span style="font-size: 18px">创建多线程的步骤:</span><br data-filtered="filtered"><span style="font-size: 18px">1、编写线程所要执行的方法</span><br data-filtered="filtered"><span style="font-size: 18px">2、实例化Thread类,并传入一个指向线程所要执行方法的委托。(这时线程已经产生,但还没有运行)</span><br data-filtered="filtered"><span style="font-size: 18px">3、调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定</span></p>
<p><span style="font-size: 18px">2.1 System.Threading.Thread类</span></p>
<p><span style="font-size: 18px">Thread类是是控制线程的基础类,位于System.Threading命名空间下,具有4个重载的构造函数:</span></p>
<div class="table-wrapper"><table border="0">
<tbody>
<tr>
<td><span style="font-size: 18px">名称</span></td>
<td><span style="font-size: 18px">说明</span></td>
</tr>
<tr dir="ltr" valign="middle" align="center">
<td style="text-align: left" valign="middle" align="center"><span style="font-size: 18px">Thread(ParameterizedThreadStart)</span></td>
<td>
<p style="text-align: left"><span id="mt2" class="sentence SentenceHover" style="font-size: 18px" data-guid="6b5962d2eab7718db4185357b53b7a28" data-source="Initializes a new instance of the <span class="selflink">Thread class, specifying a delegate that allows an object to be passed to the thread when the thread is started.">初始化 <span class="selflink">Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托。要执行的方法是有参的。</span></span></p>
</td>
</tr>
<tr>
<td><span style="font-size: 18px">Thread(ParameterizedThreadStart, Int32)</span></td>
<td><span id="mt3" class="sentence SentenceHover" style="font-size: 18px" data-guid="736339525d65510a8607cd08facb9ca6" data-source="Initializes a new instance of the <span class="selflink">Thread class, specifying a delegate that allows an object to be passed to the thread when the thread is started and specifying the maximum stack size for the thread.">初始化 <span class="selflink">Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托,并指定线程的最大堆栈大小</span></span></td>
</tr>
<tr>
<td><span style="font-size: 18px">Thread(ThreadStart)</span></td>
<td>
<p><span id="mt4" class="sentence" style="font-size: 18px" data-guid="0eb700d5132f84a53bf024e087a7d579" data-source="Initializes a new instance of the <span class="selflink">Thread class.">初始化 <span class="selflink">Thread 类的新实例。要执行的方法是无参的。</span></span></p>
</td>
</tr>
<tr>
<td><span style="font-size: 18px">Thread(ThreadStart, Int32)</span></td>
<td>
<p><span id="mt5" class="sentence SentenceHover" style="font-size: 18px" data-guid="b656e2811649bf2cf3de6978f8927aef" data-source="Initializes a new instance of the <span class="selflink">Thread class, specifying the maximum stack size for the thread.">初始化 <span class="selflink">Thread 类的新实例,指定线程的最大堆栈大小。</span></span></p>
</td>
</tr>
</tbody>
</table></div>
<p><span style="font-size: 18px">ThreadStart是一个无参的、返回值为void的委托。委托定义如下:</span></p>
<p><span style="font-size: 18px">public delegate void ThreadStart()</span></p>
<p><span style="font-size: 18px">通过ThreadStart委托创建并运行一个线程:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建无参的线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> Thread thread1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(Thread1));
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用Start方法执行线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)"> thread1.Start();
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 创建无参的方法
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Thread1()
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">这是无参的方法</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">20</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211113130210-1401793011.png"></span></p>
<p><span style="font-size: 18px">除了可以运行静态的方法,还可以运行实例方法</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建ThreadTest类的一个实例</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> ThreadTest test=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadTest();
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用test实例的MyThread方法</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> Thread thread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(test.MyThread));
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)"> thread.Start();
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ThreadTest
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> MyThread()
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">19</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">这是一个实例方法</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">21</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211114013069-1607557625.png"></span></p>
<p><span style="font-size: 18px">如果为了简单,也可以通过匿名委托或Lambda表达式来为Thread的构造方法赋值</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过匿名委托创建</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> Thread thread1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">delegate</span>() { Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">我是通过匿名委托创建的线程</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); });
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)"> thread1.Start();
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过Lambda表达式创建</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> Thread thread2 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(() => Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">我是通过Lambda表达式创建的委托</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)"> thread2.Start();
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">10</span>}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211114654257-1719449786.png"></span></p>
<p><span style="font-size: 18px">ParameterizedThreadStart是一个有参的、返回值为void的委托,定义如下:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">delegate</span> <span style="color: rgba(0, 0, 255, 1)">void</span> ParameterizedThreadStart(Object obj)</span></pre>
</div>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过ParameterizedThreadStart创建线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> Thread thread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ParameterizedThreadStart(Thread1));
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">给方法传值</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> thread.Start(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">这是一个有参数的委托</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">11</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 创建有参的方法
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 注意:方法里面的参数类型必须是Object类型
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="obj"></param></span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Thread1(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> obj)
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> Console.WriteLine(obj);
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">21</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">注意:ParameterizedThreadStart委托的参数类型必须是Object的。如果使用的是不带参数的委托,不能使用带参数的Start方法运行线程,否则系统会抛出异常。但使用带参数的委托,可以使用thread.Start()来运行线程,这时所传递的参数值为null。</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211121057147-1154783457.png"></span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211121519632-383256724.png"></span></p>
<p><span style="font-size: 18px"><strong>2.2 线程的常用属性</strong></span></p>
<div class="table-wrapper"><table class="memberListTable">
<tbody>
<tr><th><span style="font-size: 18px">属性名称</span></th><th><span style="font-size: 18px">说明</span></th></tr>
<tr valign="top">
<td><span style="font-size: 18px">CurrentContext</span></td>
<td><span style="font-size: 18px">获取线程正在其中执行的当前上下文。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">CurrentThread</span></td>
<td><span style="font-size: 18px">获取当前正在运行的线程。</span></td>
</tr>
<tr valign="top">
<td valign="middle"><span style="font-size: 18px">ExecutionContext</span></td>
<td><span style="font-size: 18px">获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">IsAlive</span></td>
<td><span style="font-size: 18px">获取一个值,该值指示当前线程的执行状态。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">IsBackground</span></td>
<td><span style="font-size: 18px">获取或设置一个值,该值指示某个线程是否为后台线程。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">IsThreadPoolThread</span></td>
<td><span style="font-size: 18px">获取一个值,该值指示线程是否属于托管线程池。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">ManagedThreadId</span></td>
<td><span style="font-size: 18px">获取当前托管线程的唯一标识符。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Name</span></td>
<td><span style="font-size: 18px">获取或设置线程的名称。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Priority</span></td>
<td><span style="font-size: 18px">获取或设置一个值,该值指示线程的调度优先级。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">ThreadState</span></td>
<td><span style="font-size: 18px">获取一个值,该值包含当前线程的状态。</span></td>
</tr>
</tbody>
</table></div>
<p><span style="font-size: 18px"><strong>2.2.1 线程的标识符</strong></span></p>
<p><span style="font-size: 18px">ManagedThreadId是确认线程的唯一标识符,程序在大部分情况下都是通过Thread.ManagedThreadId来辨别线程的。而Name是一个可变值,在默认时候,Name为一个空值 Null,开发人员可以通过程序设置线程的名称,但这只是一个辅助功能。</span></p>
<p><span style="font-size: 18px"><strong>2.2.2 线程的优先级别</strong></span></p>
<p><span style="font-size: 18px">当线程之间争夺CPU时间时,CPU按照线程的优先级给予服务。高优先级的线程可以完全阻止低优先级的线程执行。.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别。</span></p>
<div>
<div class="table-wrapper"><table id="memberList" cellpadding="1" border="0">
<tbody>
<tr><th class="nameColumn"><span style="font-size: 18px">成员名称</span></th><th class="descriptionColumn"><span style="font-size: 18px">说明</span></th></tr>
<tr>
<td><span class="selflink" style="font-size: 18px">Lowest</span></td>
<td><span id="xn2_3e53547e0e9a509aff4a76382e494083" class="sentence" style="font-size: 18px">可以将 Thread 安排在具有任何其他优先级的线程之后。</span></td>
</tr>
<tr>
<td><span class="selflink" style="font-size: 18px">BelowNormal</span></td>
<td><span id="xn3_f979b4a5dfbb5a35942312092b2cd47a" class="sentence" style="font-size: 18px">可以将 Thread 安排在具有 <span class="input">Normal 优先级的线程之后,在具有 <span class="input">Lowest 优先级的线程之前。</span></span></span></td>
</tr>
<tr>
<td><span class="selflink" style="font-size: 18px">Normal</span></td>
<td><span id="xn4_8b94d9644aacd17640f4971fc6e79dc7" class="sentence" style="font-size: 18px">默认选择。可以将 Thread 安排在具有 <span class="input">AboveNormal 优先级的线程之后,在具有 <span class="input">BelowNormal 优先级的线程之前<span id="xn5_6fa67ded483ad3d242ea365f0ac225c4" class="sentence">。</span></span></span></span></td>
</tr>
<tr>
<td><span class="selflink" style="font-size: 18px">AboveNormal</span></td>
<td><span id="xn6_d067edb8ea0b5bfd51a2591334b86fd7" class="sentence" style="font-size: 18px">可以将 Thread 安排在具有 <span class="input">Highest 优先级的线程之后,在具有 <span class="input">Normal 优先级的线程之前。</span></span></span></td>
</tr>
<tr>
<td><span class="selflink" style="font-size: 18px">Highest</span></td>
<td><span id="xn7_fc7a0e931c772d08fc98b6de2c82ab3c" class="sentence" style="font-size: 18px">可以将 Thread 安排在具有任何其他优先级的线程之前。</span></td>
</tr>
</tbody>
</table></div>
</div>
<p><span style="font-size: 18px"><strong>2.2.3 线程的状态</strong></span></p>
<p><span style="font-size: 18px">通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。</span></p>
<p><span style="font-size: 18px">前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。</span></p>
<p><span style="font-size: 18px">CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。</span></p>
<p><span style="font-size: 18px"><strong>2.2.4 System.Threading.Thread的方法</strong></span></p>
<p><span style="font-size: 18px">Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。</span></p>
<div class="table-wrapper"><table class="memberListTable"><colgroup data-filtered="filtered"><col data-filtered="filtered" width="30%"><col data-filtered="filtered" width="70%"></colgroup>
<tbody>
<tr><th><span style="font-size: 18px">方法名称</span></th><th><span style="font-size: 18px">说明</span></th></tr>
<tr valign="top">
<td><span style="font-size: 18px">Abort() </span></td>
<td><span style="font-size: 18px">终止本线程。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">GetDomain()</span></td>
<td><span style="font-size: 18px">返回当前线程正在其中运行的当前域。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">GetDomainId()</span></td>
<td><span style="font-size: 18px">返回当前线程正在其中运行的当前域Id。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Interrupt()</span></td>
<td><span style="font-size: 18px">中断处于 WaitSleepJoin 线程状态的线程。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Join()</span></td>
<td><span style="font-size: 18px">已重载。 阻塞调用线程,直到某个线程终止时为止。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Resume()</span></td>
<td><span style="font-size: 18px">继续运行已挂起的线程。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Start() </span></td>
<td><span style="font-size: 18px">执行本线程。</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Suspend()</span></td>
<td><span style="font-size: 18px">挂起当前线程,如果当前线程已属于挂起状态则此不起作用</span></td>
</tr>
<tr valign="top">
<td><span style="font-size: 18px">Sleep() </span></td>
<td><span style="font-size: 18px">把正在运行的线程挂起一段时间。</span></td>
</tr>
</tbody>
</table></div>
<p><span style="font-size: 18px">线程示例</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取正在运行的线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> Thread thread =<span style="color: rgba(0, 0, 0, 1)"> Thread.CurrentThread;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置线程的名字</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> thread.Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">主线程</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取当前线程的唯一标识符</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 255, 1)">int</span> id =<span style="color: rgba(0, 0, 0, 1)"> thread.ManagedThreadId;
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取当前线程的状态</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> ThreadState state=<span style="color: rgba(0, 0, 0, 1)"> thread.ThreadState;
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取当前线程的优先级</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> ThreadPriority priority=<span style="color: rgba(0, 0, 0, 1)"> thread.Priority;
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 255, 1)">string</span> strMsg = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread ID:{0}\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread Name:{1}\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> +
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread State:{2}\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Thread Priority:{3}\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, id, thread.Name,
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)"> state, priority);
</span><span style="color: rgba(0, 128, 128, 1)">16</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)"> Console.WriteLine(strMsg);
</span><span style="color: rgba(0, 128, 128, 1)">18</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">20</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211123413257-58776054.png"></span></p>
<p><span style="font-size: 18px"><strong>2.3 前台线程和后台线程</strong></span></p>
<p><span style="font-size: 18px">前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程</span><br data-filtered="filtered"><span style="font-size: 18px"> 都是前台线程</span><br data-filtered="filtered"><span style="font-size: 18px">后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。</span></p>
<p><span style="font-size: 18px">通过BeginXXX方法运行的线程都是后台线程。</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">演示前台、后台线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> BackGroundTest background = <span style="color: rgba(0, 0, 255, 1)">new</span> BackGroundTest(<span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建前台线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> Thread fThread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(background.RunLoop));
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">给线程命名</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> fThread.Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">前台线程</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">11</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> BackGroundTest background1 = <span style="color: rgba(0, 0, 255, 1)">new</span> BackGroundTest(<span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建后台线程</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> Thread bThread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(background1.RunLoop));
</span><span style="color: rgba(0, 128, 128, 1)">16</span> bThread.Name = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">后台线程</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置为后台线程</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> bThread.IsBackground = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">19</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)"> fThread.Start();
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)"> bThread.Start();
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">25</span>
<span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> BackGroundTest
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> Count;
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 255, 1)">public</span> BackGroundTest(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> count)
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.Count =<span style="color: rgba(0, 0, 0, 1)"> count;
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> RunLoop()
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取当前线程的名称</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 255, 1)">string</span> threadName =<span style="color: rgba(0, 0, 0, 1)"> Thread.CurrentThread.Name;
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < Count; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">39</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{0}计数:{1}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,threadName,i.ToString());
</span><span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">线程休眠500毫秒</span>
<span style="color: rgba(0, 128, 128, 1)">41</span> Thread.Sleep(<span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">43</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{0}完成计数</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,threadName);
</span><span style="color: rgba(0, 128, 128, 1)">44</span>
<span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">46</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:前台线程执行完,后台线程未执行完,程序自动结束。</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211150321319-1313375658.png"></span></p>
<p><span style="font-size: 18px">把bThread.IsBackground = true注释掉,运行结果:主线程执行完毕后(Main函数),程序并未结束,而是要等所有的前台线程结束以后才会结束。</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211145848647-1856795810.png"></span></p>
<p><span style="font-size: 18px">后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。</span></p>
<p><span style="font-size: 18px"><strong>2.4 线程同步</strong></span></p>
<p><span style="font-size: 18px">所谓同步:是指在某一时刻只有一个线程可以访问变量。</span><br data-filtered="filtered"><span style="font-size: 18px">如果不能确保对变量的访问是同步的,就会产生错误。</span><br data-filtered="filtered"><span style="font-size: 18px">c#为同步访问变量提供了一个非常简单的方式,即使用c#语言的关键字Lock,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在c#中,关键字Lock定义如下:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">Lock(expression)
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)"> statement_block
</span><span style="color: rgba(0, 128, 128, 1)">4</span> }</span></pre>
</div>
<p><span style="font-size: 18px">expression代表你希望跟踪的对象:</span><br data-filtered="filtered"><span style="font-size: 18px"> 如果你想保护一个类的实例,一般地,你可以使用this;</span><br data-filtered="filtered"><span style="font-size: 18px"> 如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了</span><br data-filtered="filtered"><span style="font-size: 18px">而statement_block就算互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。</span></p>
<p><span style="font-size: 18px">以书店卖书为例</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> BookShop book = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BookShop();
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建两个线程同时访问Sale方法</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> Thread t1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(book.Sale));
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> Thread t2 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(book.Sale));
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)"> t1.Start();
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)"> t2.Start();
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">15</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> BookShop
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">剩余图书数量</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> num = <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Sale()
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 255, 1)">int</span> tmp =<span style="color: rgba(0, 0, 0, 1)"> num;
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (tmp > <span style="color: rgba(128, 0, 128, 1)">0</span>)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断是否有书,如果有就可以卖</span>
<span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">27</span> Thread.Sleep(<span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">28</span> num -= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">29</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">售出一本图书,还剩余{0}本</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, num);
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">33</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">没有了</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">36</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211162002257-1617971456.png"></span></p>
<p><span style="font-size: 18px">从运行结果可以看出,两个线程同步访问共享资源,没有考虑同步的问题,结果不正确。</span></p>
<p><span style="font-size: 18px">考虑线程同步,改进后的代码:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Program
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Main(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">[] args)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> BookShop book = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BookShop();
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建两个线程同时访问Sale方法</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> Thread t1 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(book.Sale));
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> Thread t2 = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(book.Sale));
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">启动线程</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)"> t1.Start();
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)"> t2.Start();
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)"> Console.ReadKey();
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">15</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> BookShop
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">剩余图书数量</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> num = <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Sale()
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用lock关键字解决线程同步问题</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 255, 1)">lock</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 255, 1)">int</span> tmp =<span style="color: rgba(0, 0, 0, 1)"> num;
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (tmp > <span style="color: rgba(128, 0, 128, 1)">0</span>)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断是否有书,如果有就可以卖</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">30</span> Thread.Sleep(<span style="color: rgba(128, 0, 128, 1)">1000</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">31</span> num -= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">32</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">售出一本图书,还剩余{0}本</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, num);
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">36</span> Console.WriteLine(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">没有了</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">40</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211162532429-1303317997.png"></span></p>
<p><span style="font-size: 18px"><strong>2.5 跨线程访问</strong></span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211165843304-141162350.png"></span></p>
<p><span style="font-size: 18px">点击“测试”,创建一个线程,从0循环到10000给文本框赋值,代码如下:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btn_Test_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建一个线程去执行这个方法:创建的线程默认是前台线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> Thread thread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(Test));
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将线程设置为后台线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> thread.IsBackground = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)"> thread.Start();
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">10</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Test()
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">10000</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.textBox1.Text =<span style="color: rgba(0, 0, 0, 1)"> i.ToString();
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">17</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2015.cnblogs.com/blog/1033738/201612/1033738-20161211170247710-1207822491.png"></span></p>
<p><span style="font-size: 18px">产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。</span></p>
<p><span style="font-size: 18px">解决方案:</span></p>
<p><span style="font-size: 18px">1、在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Form1_Load(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">取消跨线程的访问</span>
<span style="color: rgba(0, 128, 128, 1)">4</span> Control.CheckForIllegalCrossThreadCalls = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">5</span>}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。</span></p>
<p><span style="font-size: 18px">2、使用回调函数</span></p>
<p><span style="font-size: 18px">回调实现的一般过程:</span></p>
<p><span style="font-size: 18px"> C#的方法回调机制,也是建立在委托基础上的,下面给出它的典型实现过程。</span></p>
<p><span style="font-size: 18px">(1)、定义、声明回调。</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义回调</span>
<span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">delegate</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> DoSomeCallBack(Type para);
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明回调</span>
<span style="color: rgba(0, 128, 128, 1)">4</span> DoSomeCallBack doSomaCallBack;</span></pre>
</div>
<p><span style="font-size: 18px">可以看出,这里定义声明的“回调”(doSomaCallBack)其实就是一个委托。</span></p>
<p><span style="font-size: 18px">(2)、初始化回调方法。</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px">doSomeCallBack=<span style="color: rgba(0, 0, 255, 1)">new</span> DoSomeCallBack(DoSomeMethod);</span></pre>
</div>
<p><span style="font-size: 18px">所谓“初始化回调方法”实际上就是实例化刚刚定义了的委托,这里作为参数的DoSomeMethod称为“回调方法”,它封装了对另一个线程中目标对象(窗体控件或其他类)的操作代码。</span></p>
<p><span style="font-size: 18px">(3)、触发对象动作</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px">Optobj.Invoke(doSomeCallBack,arg);</span></pre>
</div>
<p><span style="font-size: 18px">其中Opt obj为目标操作对象,在此假设它是某控件,故调用其Invoke方法。Invoke方法签名为:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 18px"><span style="color: rgba(0, 0, 255, 1)">object</span>Control.Invoke(Delegatemethod,<span style="color: rgba(0, 0, 255, 1)">params</span><span style="color: rgba(0, 0, 255, 1)">object</span>[] args);</span></pre>
</div>
<p><span style="font-size: 18px">它的第一个参数为委托类型,可见“触发对象动作”的本质,就是把委托doSomeCallBack作为参数传递给控件的Invoke方法,这与委托的使用方式是一模一样的。</span></p>
<p><span style="font-size: 18px">最终作用于对象Opt obj的代码是置于回调方法体DoSomeMethod()中的,如下所示:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> DoSomeMethod(type para)
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">方法体</span>
<span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)"> Opt obj.someMethod(para);
</span><span style="color: rgba(0, 128, 128, 1)">5</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">如果不用回调,而是直接在程序中使用“Opt obj.someMethod(para);”,则当对象Opt obj不在本线程(跨线程访问)时就会发生上面所示的错误。</span></p>
<p><span style="font-size: 18px">从以上回调实现的一般过程可知:C#的回调机制,实质上是委托的一种应用。在C#网络编程中,回调的应用是非常普遍的,有了方法回调,就可以在.NET上写出线程安全的代码了。</span></p>
<p><span style="font-size: 18px">使用方法回调,实现给文本框赋值:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> MultiThreadDemo
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Form1 : Form
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Form1()
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)"> InitializeComponent();
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义回调</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">delegate</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setTextValueCallBack(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> value);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明回调</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> setTextValueCallBack setCallBack;
</span><span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btn_Test_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">实例化回调</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> setCallBack = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> setTextValueCallBack(SetValue);
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建一个线程去执行这个方法:创建的线程默认是前台线程</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> Thread thread = <span style="color: rgba(0, 0, 255, 1)">new</span> Thread(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ThreadStart(Test));
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Start方法标记这个线程就绪了,可以随时被执行,具体什么时候执行这个线程,由CPU决定
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将线程设置为后台线程</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> thread.IsBackground = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)"> thread.Start();
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">26</span>
<span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Test()
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">10000</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用回调</span>
<span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)"> textBox1.Invoke(setCallBack, i);
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">35</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 定义回调使用的方法
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="value"></param></span>
<span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> SetValue(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> value)
</span><span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.textBox1.Text =<span style="color: rgba(0, 0, 0, 1)"> value.ToString();
</span><span style="color: rgba(0, 128, 128, 1)">43</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">44</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">45</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px"><strong> 2.6 终止线程</strong></span></p>
<p><span style="font-size: 18px">若想终止正在运行的线程,可以使用Abort()方法。</span></p>
<p><span style="font-size: 18px"><strong>三、同步和异步</strong></span></p>
<p><span style="font-size: 18px">同步和异步是对方法执行顺序的描述。</span></p>
<p><span style="font-size: 18px">同步:等待上一行完成计算之后,才会进入下一行。</span></p>
<p><span style="font-size: 18px">例如:请同事吃饭,同事说很忙,然后就等着同事忙完,然后一起去吃饭。</span></p>
<p><span style="font-size: 18px">异步:不会等待方法的完成,会直接进入下一行,是非阻塞的。</span></p>
<p><span style="font-size: 18px">例如:请同事吃饭,同事说很忙,那同事先忙,自己去吃饭,同事忙完了他自己去吃饭。</span></p>
<p><span style="font-size: 18px">下面通过一个例子讲解同步和异步的区别</span></p>
<p><span style="font-size: 18px">1、新建一个winform程序,上面有两个按钮,一个同步方法、一个异步方法,在属性里面把输出类型改成控制台应用程序,这样可以看到输出结果,代码如下:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System;
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Collections.Generic;
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.ComponentModel;
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Data;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Drawing;
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Linq;
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Text;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Threading;
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Threading.Tasks;
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 255, 1)">using</span><span style="color: rgba(0, 0, 0, 1)"> System.Windows.Forms;
</span><span style="color: rgba(0, 128, 128, 1)">11</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> MyAsyncThreadDemo
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">partial</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Form1 : Form
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Form1()
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)"> InitializeComponent();
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">20</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 异步方法
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="sender"></param></span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="e"></param></span>
<span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsync_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">28</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">***************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">29</span> Action<<span style="color: rgba(0, 0, 255, 1)">string</span>> action = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong;
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用委托(同步调用)</span>
<span style="color: rgba(0, 128, 128, 1)">31</span> action.Invoke(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsync_Click_1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 异步调用委托</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> action.BeginInvoke(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsync_Click_2</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(0, 0, 255, 1)">null</span>,<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">34</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">***************btnAsync_Click End {Thread.CurrentThread.ManagedThreadId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">36</span>
<span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 同步方法
</span><span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="sender"></param></span>
<span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="e"></param></span>
<span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnSync_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)">43</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">44</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 255, 1)">int</span> j = <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">46</span> <span style="color: rgba(0, 0, 255, 1)">int</span> k = <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">47</span> <span style="color: rgba(0, 0, 255, 1)">int</span> m = j +<span style="color: rgba(0, 0, 0, 1)"> k;
</span><span style="color: rgba(0, 128, 128, 1)">48</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">5</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">49</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">50</span> <span style="color: rgba(0, 0, 255, 1)">string</span> name = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnSync_Click_{i}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">51</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong(name);
</span><span style="color: rgba(0, 128, 128, 1)">52</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">53</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">54</span>
<span style="color: rgba(0, 128, 128, 1)">55</span>
<span style="color: rgba(0, 128, 128, 1)">56</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> DoSomethingLong(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> name)
</span><span style="color: rgba(0, 128, 128, 1)">57</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">58</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************DoSomethingLong {name} Start {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">59</span> <span style="color: rgba(0, 0, 255, 1)">long</span> lResult = <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">60</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">1000000000</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">61</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">62</span> lResult +=<span style="color: rgba(0, 0, 0, 1)"> i;
</span><span style="color: rgba(0, 128, 128, 1)">63</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">64</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************DoSomethingLong {name} End {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {lResult}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">65</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">66</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">67</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">2、启动程序,点击同步,结果如下:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531000901060-1531337026.png"></span></p>
<p><span style="font-size: 18px">从上面的截图中能够很清晰的看出:同步方法是等待上一行代码执行完毕之后才会执行下一行代码。</span></p>
<p><span style="font-size: 18px">点击异步,结果如下:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531001146112-1822723215.png"></span></p>
<p><span style="font-size: 18px">从上面的截图中看出:当执行到action.BeginInvoke("btnAsync_Click_2",null,null);这句代码的时候,程序并没有等待这段代码执行完就执行了下面的End,没有阻塞程序的执行。</span></p>
<p><span style="font-size: 18px">在刚才的测试中,如果点击同步,这时winform界面不能拖到,界面卡住了,是因为主线程(即UI线程)在忙于计算。</span></p>
<p><span style="font-size: 18px">点击异步的时候,界面不会卡住,这是因为主线程已经结束,计算任务交给子线程去做。</span></p>
<p><span style="font-size: 18px">在仔细检查上面两个截图,可以看出异步的执行速度比同步执行速度要快。同步方法执行完将近16秒,异步方法执行完将近6秒。</span></p>
<p><span style="font-size: 18px">在看下面的一个例子,修改异步的方法,也和同步方法一样执行循环,修改后的代码如下:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsync_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">***************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Action<string> action = this.DoSomethingLong;</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)">/ 调用委托(同步调用)</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">action.Invoke("btnAsync_Click_1");</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)">/ 异步调用委托</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">action.BeginInvoke("btnAsync_Click_2",null,null);</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> Action<<span style="color: rgba(0, 0, 255, 1)">string</span>> action = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong;
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">5</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Thread.Sleep(5);</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 255, 1)">string</span> name = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsync_Click_{i}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">14</span> action.BeginInvoke(name, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 128, 1)">16</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">***************btnAsync_Click End {Thread.CurrentThread.ManagedThreadId}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">17</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">结果如下:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531005521273-405746523.png"></span></p>
<p><span style="font-size: 18px">从截图中能够看出:同步方法执行是有序的,异步方法执行是无序的。异步方法无序包括启动无序和结束无序。启动无序是因为同一时刻向操作系统申请线程,操作系统收到申请以后,返回执行的顺序是无序的,所以启动是无序的。结束无序是因为虽然线程执行的是同样的操作,但是每个线程的耗时是不同的,所以结束的时候不一定是先启动的线程就先结束。从上面同步方法中可以清晰的看出:btnSync_Click_0执行时间耗时不到3秒,而btnSync_Click_1执行时间耗时超过了3秒。可以想象体育比赛中的跑步,每位运动员听到发令枪起跑的顺序不同,每位运动员花费的时间不同,最终到达终点的顺序也不同。</span></p>
<p><span style="font-size: 18px">总结一下同步方法和异步方法的区别:</span></p>
<p><span style="font-size: 18px">1、同步方法由于主线程忙于计算,所以会卡住界面。</span></p>
<p><span style="font-size: 18px"> 异步方法由于主线程执行完了,其他计算任务交给子线程去执行,所以不会卡住界面,用户体验性好。</span></p>
<p><span style="font-size: 18px">2、同步方法由于只有一个线程在计算,所以执行速度慢。</span></p>
<p><span style="font-size: 18px"> 异步方法由多个线程并发运算,所以执行速度快,但并不是线性增长的(资源可能不够)。多线程也不是越多越好,只有多个独立的任务同时运行,才能加快速度。</span></p>
<p><span style="font-size: 18px">3、同步方法是有序的。</span></p>
<p><span style="font-size: 18px"> 异步多线程是无序的:启动无序,执行时间不确定,所以结束也是无序的。一定不要通过等待几毫秒的形式来控制线程启动/执行时间/结束。</span></p>
<p><span style="font-size: 18px"><strong>四、回调</strong></span></p>
<p><span style="font-size: 18px">先来看看异步多线程无序的例子:</span></p>
<p><span style="font-size: 18px">在界面上新增一个按钮,实现代码如下:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsyncAdvanced_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)">3</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">4</span> Action<<span style="color: rgba(0, 0, 255, 1)">string</span>> action = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong;
</span><span style="color: rgba(0, 128, 128, 1)">5</span> action.BeginInvoke(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsyncAdvanced_Click</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 需求:异步多线程执行完之后再打印出下面这句</span>
<span style="color: rgba(0, 128, 128, 1)">7</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}。</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">8</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">9</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531222556509-934483516.png"></span></p>
<p><span style="font-size: 18px">从上面的截图中看出,最终的效果并不是我们想要的效果,而且打印输出的还是主线程。</span></p>
<p><span style="font-size: 18px">既然异步多线程是无序的,那我们有没有什么办法可以解决无序的问题呢?办法当然是有的,那就是使用回调,.NET框架已经帮我们实现了回调:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531222937262-978905414.png"></span></p>
<p><span style="font-size: 18px">BeginInvoke的第二个参数就是一个回调,那么AsyncCallback究竟是什么呢?F12查看AsyncCallback的定义:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531223410355-1241571657.png"></span></p>
<p><span style="font-size: 18px">发现AsyncCallback就是一个委托,参数类型是IAsyncResult,明白了AsyncCallback是什么以后,将上面的代码进行如下的改造:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsyncAdvanced_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> Action<<span style="color: rgba(0, 0, 255, 1)">string</span>> action = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong;
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个回调</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> AsyncCallback callback = p =>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}。</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)"> };
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回调作为参数</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> action.BeginInvoke(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsyncAdvanced_Click</span><span style="color: rgba(128, 0, 0, 1)">"</span>, callback, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">12</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">13</span>}</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">运行结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531220700037-2010340340.png"> </span></p>
<p><span style="font-size: 18px">上面的截图中可以看出,这就是我们想要的效果,而且打印是子线程输出的,但是程序究竟是怎么实现的呢?我们可以进行如下的猜想:</span></p>
<p><span style="font-size: 18px">程序执行到BeginInvoke的时候,会申请一个基于线程池的线程,这个线程会完成委托的执行(在这里就是执行DoSomethingLong()方法),在委托执行完以后,这个线程又会去执行callback回调的委托,执行callback委托需要一个IAsyncResult类型的参数,这个IAsyncResult类型的参数是如何来的呢?鼠标右键放到BeginInvoke上面,查看返回值:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531224915455-1536213041.png"></span></p>
<p><span style="font-size: 18px">发现BeginInvoke的返回值就是IAsyncResult类型的。那么这个返回值是不是就是callback委托的参数呢?将代码进行如下的修改:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsyncAdvanced_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 需求:异步多线程执行完之后再打印出下面这句</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> Action<<span style="color: rgba(0, 0, 255, 1)">string</span>> action = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.DoSomethingLong;
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 无序的
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">action.BeginInvoke("btnAsyncAdvanced_Click", null, null);</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> IAsyncResult asyncResult = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个回调</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> AsyncCallback callback = p =>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 比较两个变量是否是同一个</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> Console.WriteLine(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)">.ReferenceEquals(p,asyncResult));
</span><span style="color: rgba(0, 128, 128, 1)">15</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}。</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)"> };
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回调作为参数</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> asyncResult= action.BeginInvoke(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">btnAsyncAdvanced_Click</span><span style="color: rgba(128, 0, 0, 1)">"</span>, callback, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">19</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 128, 1)">00</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)} {DateTime.Now.ToString(</span><span style="color: rgba(128, 0, 0, 1)">"</span>yyyy-MM-dd HH:mm:ss.fff<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)}***************</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">20</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">结果:</span></p>
<p><span style="font-size: 18px"><img src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531225633112-1377569667.png"></span></p>
<p><span style="font-size: 18px">这里可以看出BeginInvoke的返回值就是callback委托的参数。</span></p>
<p><span style="font-size: 18px">现在我们可以使用回调解决异步多线程无序的问题了。</span></p>
<p><span style="font-size: 18px">2、获取委托异步调用的返回值</span></p>
<p><span style="font-size: 18px">使用EndInvoke可以获取委托异步调用的返回值,请看下面的例子:</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img alt="复制代码" data-src="//common.cnblogs.com/images/copycode.gif"></span></div>
<pre><span style="font-size: 18px"><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> btnAsyncReturnVlaue_Click(<span style="color: rgba(0, 0, 255, 1)">object</span><span style="color: rgba(0, 0, 0, 1)"> sender, EventArgs e)
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义一个无参数、int类型返回值的委托</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> Func<<span style="color: rgba(0, 0, 255, 1)">int</span>> func = () =>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> Thread.Sleep(<span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> DateTime.Now.Day;
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)"> };
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出委托同步调用的返回值</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">func.Invoke()={func.Invoke()}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 委托的异步调用</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> IAsyncResult asyncResult = func.BeginInvoke(p =>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)"> Console.WriteLine(p.AsyncState);
</span><span style="color: rgba(0, 128, 128, 1)">15</span> },<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">异步调用返回值</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 输出委托异步调用的返回值</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">func.EndInvoke(asyncResult)={func.EndInvoke(asyncResult)}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">18</span> }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><img src="//common.cnblogs.com/images/copycode.gif"></span></div></div>
<p><span style="font-size: 18px">结果:</span></p>
<p><span style="font-size: 18px"><img alt="" class="medium-zoom-image lazyload" data-src="https://images2018.cnblogs.com/blog/1033738/201805/1033738-20180531233248436-1383129954.png"></span></p>
</div>
</div>
<div id="MySignature" role="contentinfo">
重复就是力量,数量堆死质量<br><br>
来源:https://www.cnblogs.com/HomeSapiens/p/16366835.html
頁:
[1]