多线程(Multi Threading)
线程被定义为程序的执行路径。 每个线程定义一个独特的控制流。 如果您的应用程序涉及复杂且耗时的操作(如数据库访问或某些强烈的I/O操作),那么设置不同的执行路径或线程通常很有帮助,每个线程执行特定的工作。
线程是轻量级进程。 使用线程的一个常见示例是现代操作系统的并发编程的实现。 线程的使用可以节省CPU周期的浪费并提高应用程序的效率。
到目前为止,我们编译了程序,其中单个线程作为单个进程运行,该进程是应用程序的运行实例。 但是,这样应用程序可以一次执行一个作业。 为了使它一次执行多个任务,可以将其划分为更小的线程。
在.Net中,线程通过'System.Threading'命名空间来处理。 创建System.Threading.Thread类型的变量允许您创建一个新线程以开始使用。 它允许您创建和访问程序中的各个线程。
创建线程
通过创建Thread对象来创建线程,为其构造函数提供ThreadStart引用。
ThreadStart childthreat = new ThreadStart(childthreadcall);
线程生命周期
线程的生命周期在创建System.Threading.Thread类的对象时开始,在线程终止或完成执行时结束。
以下是线程生命周期中的各种状态:
The Unstarted State :创建线程实例但未调用Start方法的情况。
The Ready State :线程准备好执行并等待CPU周期的情况。
The Not Runnable State :线程不可运行,时间:
- 已经调用了睡眠方法
- 等待方法已被调用
- 被I/O操作阻止
The Dead State :线程已完成执行或已中止的情况。
线程优先级
Thread类的Priority属性指定一个线程相对于其他线程的优先级。 .Net运行时选择具有最高优先级的就绪线程。
优先事项可归类为:
- 超出正常水平
- 低于一般
- Highest
- Lowest
- Normal
创建线程后,使用线程类的Priority属性设置其优先级。
NewThread.Priority = ThreadPriority.Highest;
线程属性和方法
Thread类具有以下重要属性:
属性 | 描述 |
---|---|
CurrentContext | 获取线程正在执行的当前上下文。 |
CurrentCulture | 获取或设置当前线程的区域性。 |
CurrentPrinciple | 获取或设置线程的基于角色的安全性的当前主体。 |
CurrentThread | 获取当前正在运行的线程。 |
CurrentUICulture | 获取或设置资源管理器用于在运行时查找特定于文化的资源的当前区域性。 |
ExecutionContext | 获取一个ExecutionContext对象,该对象包含有关当前线程的各种上下文的信息。 |
IsAlive | 获取一个值,该值指示当前线程的执行状态。 |
IsBackground | 获取或设置一个值,该值指示线程是否为后台线程。 |
IsThreadPoolThread | 获取一个值,该值指示线程是否属于托管线程池。 |
ManagedThreadId | 获取当前托管线程的唯一标识符。 |
Name | 获取或设置线程的名称。 |
Priority | 获取或设置一个值,该值指示线程的调度优先级。 |
ThreadState | 获取包含当前线程状态的值。 |
Thread类有以下重要方法:
方法 | 描述 |
---|---|
Abort | 在调用它的线程中引发ThreadAbortException,以开始终止线程的过程。 调用此方法通常会终止该线程。 |
AllocateDataSlot | 在所有线程上分配一个未命名的数据槽。 为了获得更好的性能,请使用标有ThreadStaticAttribute属性的字段。 |
AllocateNamedDataSlot | 在所有线程上分配命名数据槽。 为了获得更好的性能,请使用标有ThreadStaticAttribute属性的字段。 |
BeginCriticalRegion | 通知主机执行即将进入代码区域,其中线程中止或未处理异常的影响可能危及应用程序域中的其他任务。 |
BeginThreadAffinity | 通知主机托管代码即将执行依赖于当前物理操作系统线程标识的指令。 |
EndCriticalRegion | 通知主机执行即将进入代码区域,其中线程中止或未处理异常的影响仅限于当前任务。 |
EndThreadAffinity | 通知主机托管代码已完成执行依赖于当前物理操作系统线程标识的指令。 |
FreeNamedDataSlot | 消除进程中所有线程的名称和插槽之间的关联。 为了获得更好的性能,请使用标有ThreadStaticAttribute属性的字段。 |
GetData | 从当前线程的当前域中的当前线程的指定槽中检索值。 为了获得更好的性能,请使用标有ThreadStaticAttribute属性的字段。 |
GetDomain | 返回当前线程正在运行的当前域。 |
GetDomainID | 返回唯一的应用程序域标识符。 |
GetNamedDataSlot | 查找命名数据槽。 为了获得更好的性能,请使用标有ThreadStaticAttribute属性的字段。 |
Interrupt | 中断处于WaitSleepJoin线程状态的线程。 |
Join | 阻塞调用线程直到线程终止,同时继续执行标准COM和SendMessage抽取。 此方法具有不同的重载形式。 |
MemoryBarrier | 按如下方式同步内存访问:执行当前线程的处理器无法重新排序指令,使得在调用MemoryBarrier之前的内存访问在对MemoryBarrier的调用之后的内存访问之后执行。 |
ResetAbort | 取消当前线程请求的中止。 |
SetData | 为当前运行的线程设置该线程当前域的指定槽中的数据。 为了获得更好的性能,请使用标记为ThreadStaticAttribute属性的字段。 |
Start | Starts a thread. |
Sleep | 使线程暂停一段时间。 |
SpinWait | 导致线程等待iterations参数定义的次数。 |
VolatileRead() | 读取字段的值。 该值是计算机中任何处理器写入的最新值,无论处理器数量或处理器高速缓存状态如何。 此方法具有不同的重载形式。 |
VolatileWrite() | 立即将值写入字段,以便该值对计算机中的所有处理器可见。 此方法具有不同的重载形式。 |
Yield | 使调用线程执行到另一个准备在当前处理器上运行的线程。 操作系统选择要生成的线程。 |
例子 (Example)
以下示例说明了Thread类的用法。 该页面有一个标签控件,用于显示来自子线程的消息。 使用Response.Write()方法直接显示来自主程序的消息。 因此它们出现在页面顶部。
源文件如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="threaddemo._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>
Untitled Page
</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>Thread Example</h3>
</div>
<asp:Label ID="lblmessage" runat="server" Text="Label">
</asp:Label>
</form>
</body>
</html>
文件背后的代码如下:
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;
namespace threaddemo
{
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并终止。
控制返回主线程。
执行时,程序发送以下消息: