线程被定义为程序的执行路径。 每个线程定义一个独特的控制流程。 如果应用程序涉及复杂和耗时的操作(如数据库访问或某些紧张的I/O操作),那么设置不同的执行路径或线程通常会很有帮助,每个线程都执行特定的工作。
线程是轻量级的过程。 使用线程的一个常见例子是由现代操作系统实现并发编程。 线程的使用节省了CPU周期的浪费并提高了应用程序的效率。
到目前为止,我们编译的程序中,是以单个线程作为应用程序运行的实例来运行。 但是,这样应用程序一次可以只可执行一项工作。 为了一次执行多个任务,可以将其分成更小的线程。
在.Net中,线程是通过System.Threading
命名空间处理的。 创建System.Threading.Thread
类型的变量允许您创建一个新的线程开始使用。它允许创建和访问程序中的各个线程。
创建线程
一个线程是通过创建一个Thread
类的对象来创建的,给它的构造器一个ThreadStart
引用。
ThreadStart childthreat = new ThreadStart(childthreadcall);
线程生命周期
线程的生命周期在System.Threading.Thread
类的对象被创建时开始,并在线程被终止或完成执行时结束。
以下是线程生命周期中的各种状态:
- 未开始状态:创建线程实例但未启动
Start
方法的情况。 - 就绪状态:线程准备执行并等待CPU周期的情况。
- 不可运行状态:一个线程不可运行,当:
Sleep
方法已被调用Wait
方法已被调用- 被I/O操作阻止
- 死亡状态:线程完成执行或被中止的情况。
线程优先
Thread
类的Priority
属性指定一个线程相对于其他线程的优先级。 .Net运行时选择具有最高优先级的就绪线程。
优先级可以被分类为:
- 超出正常水平
- 低于一般
- 最高
- 最低
- 正常
当线程被创建,它的优先级就使用线程类的Priority
属性来设置。
NewThread.Priority = ThreadPriority.Highest;
线程属性和方法
Thread
类具有以下重要的属性:
编号 | 属性 | 描述 |
---|---|---|
1 | CurrentContext |
获取线程正在执行的当前上下文。 |
2 | CurrentCulture |
获取或设置当前线程的文化。 |
3 | CurrentPrinciple |
获取或设置线程当前的基于角色的安全性的主体。 |
4 | CurrentThread |
获取当前正在运行的线程。 |
5 | CurrentUICulture |
获取或设置资源管理器在运行时查找文化特定资源的当前文化。 |
6 | ExecutionContext |
获取一个ExecutionContext 对象,其中包含有关当前线程的各种上下文的信息。 |
7 | IsAlive |
获取一个指示当前线程执行状态的值。 |
8 | IsBackground |
获取或设置一个值,该值指示线程是否为后台线程。 |
9 | IsThreadPoolThread |
获取一个值,该值指示线程是否属于托管线程池。 |
10 | ManagedThreadId |
获取当前托管线程的唯一标识符。 |
11 | Name |
获取或设置线程的名称。 |
12 | Priority |
获取或设置一个指示线程调度优先级的值。 |
13 | ThreadState |
获取包含当前线程状态的值。 |
Thread
类具有以下重要的方法:
编号 | 方法 | 描述 |
---|---|---|
1 | Abort |
在调用它的线程中引发ThreadAbortException 异常,以开始终止线程的过程。 调用这个方法通常会终止线程。 |
2 | AllocateDataSlot |
在所有线程上分配一个未命名的数据插槽。为了获得更好的性能,请使用ThreadStaticAttribute 属性标记字段。 |
3 | AllocateNamedDataSlot |
在所有线程上分配一个指定的数据插槽。 为了获得更好的性能,请使用ThreadStaticAttribute 属性标记字段。 |
4 | BeginCriticalRegion |
通知主机执行即将进入一个线程中止或未处理异常的影响可能危及应用程序域中的其他任务的代码区域。 |
5 | BeginThreadAffinity |
通知主机托管代码即将执行取决于当前物理操作系统线程标识的指令。 |
6 | EndCriticalRegion |
通知主机执行即将进入一个线程中止或未处理异常的影响被限制在当前任务的代码区域。 |
7 | EndThreadAffinity |
通知主机,托管代码已经完成执行取决于当前物理操作系统线程的标识的指令。 |
8 | FreeNamedDataSlot |
消除进程中所有线程的名称和插槽之间的关联。 为了获得更好的性能,请使用标记为ThreadStaticAttribute 属性字段。 |
9 | GetData |
从当前线程的当前域内的当前线程的指定插槽中检索值。 为了获得更好的性能,请使用标记为ThreadStaticAttribute 属性字段。 |
10 | GetDomain |
返回当前线程正在运行的当前域。 |
11 | GetDomainID |
返回唯一的应用程序域标识符。 |
12 | GetNamedDataSlot |
查找一个命名的数据插槽。 为了获得更好的性能,请使用ThreadStaticAttribute 属性标记字段。 |
13 | Interrupt |
中断处于WaitSleepJoin 线程状态的线程。 |
14 | Join |
阻塞调用线程,直到线程终止,同时继续执行标准COM和SendMessage 抽取。这种方法有不同的重载形式。 |
15 | MemoryBarrier |
同步内存访问,如下所示:执行当前线程的处理器不能重新排序指令,以便在调用MemoryBarrier 之前执行内存访问之后,访问MemoryBarrier 调用之后的内存访问。 |
16 | ResetAbort |
取消当前线程所请求的中止。 |
17 | SetData |
在当前正在运行的线程的指定槽中设置该线程当前域的数据。为了获得更好的性能,请使用标记为ThreadStaticAttribute 属性的字段。 |
18 | Start |
开始一个线程。 |
19 | Sleep |
使线程暂停一段时间。 |
20 | SpinWait |
使线程等待迭代参数定义的次数。 |
21 | VolatileRead() |
读取一个字段的值。该值是计算机中任何处理器写入的最新值,无论处理器的数量或处理器高速缓存的状态如何。这种方法有不同的重载形式。 |
22 | VolatileWrite() |
立即将值写入字段,以便计算机中的所有处理器都可以看到该值。 这种方法有不同的重载形式。 |
23 | Yield |
使调用线程产生执行到另一个准备在当前处理器上运行的线程。 操作系统选择要产生的线程。 |
示例
以下示例说明了Thread
类的用法。该页面具有用于显示来自子线程的消息的标签控件。 来自主程序的消息直接使用Response.Write()
方法显示。 因此,它们出现在页面的顶部。
首先打开Visual Studio,创建一个名称为:MultiThreading 的ASP.Net空网站 项目。并添加一个Web窗体文件 - Default.aspx
源文件(Default.aspx)如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>ASP.Net多线程示例</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>ASP.Net多线程示例</h3>
</div>
<asp:Label ID="lblmessage" runat="server" Text="Label">
</asp:Label>
</form>
</body>
</html>
后端代码文件(Default.aspx.cs)如下:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Threading;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ThreadStart childthreat = new ThreadStart(childthreadcall);
Response.Write("Child Thread Started <br/>");
Thread child = new Thread(childthreat);
child.Start();
Response.Write("Main sleeping for 2 seconds.......<br/>");
Thread.Sleep(2000);
Response.Write("<br/>Main aborting child thread<br/>");
child.Abort();
}
public void childthreadcall()
{
try
{
lblmessage.Text = "<br />Child thread started <br/>";
lblmessage.Text += "Child Thread: Coiunting to 10";
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
lblmessage.Text += "<br/> in Child thread </br>";
}
lblmessage.Text += "<br/> child thread finished";
}
catch (ThreadAbortException e)
{
lblmessage.Text += "<br /> child thread - exception";
}
finally
{
lblmessage.Text += "<br /> child thread - unable to catch the exception";
}
}
}
请注意以下几点
- 加载页面时,将使用
childthreadcall()
方法来启动一个新线程。主线程活动直接显示在网页上。 - 第二个线程运行并将消息发送到标签控件。
- 主线程休眠
2000
毫秒,在此期间子线程执行。 - 子线程一直运行到主线程中止。它引发
ThreadAbortException
异常并终止。 - 控制权返回到主线程。
运行项目程序后,得到以下结果 -