创建线程
在C#中创建线程时,首先需要创建一个ThreadStart委托实例,再以这个ThreadStart委托作为为参数,来构造Thread实例。
注意:Thread类拥有四种重载的构造函数,常用的一个函数接收一个ThreadStart类型的参数,而ThreadStart是一个委托,其语法格式如下:
public delegate void ThreadStart()
线程休眠
线程的休眠是通过Thread类的Sleep方法实现的,而Thread类的实例的IsAlive属性可以判断线程是否执行完毕。Sleep方法有以下两种重载形式。
(1)将当前线程挂起指定的毫秒数,语法格式如下:
public static void Sleep (int millisecondsTimeout)
millisecondsTimeout:线程被阻止的毫秒数。如果该参数的值为零,则该线程会将其时间的剩余部分让给任何已经准备好运行的、具有同等优先级的线程,否则会无限期阻止线程。
(2)将当前线程挂起指定的时间,语法格式如下:
public static void Sleep (TimeSpan timeout)
timeout:线程被阻止的时间量的TimeSpan。
编写程序,创建线程,并在运行时休眠5秒。
using System;
using System.Threading;
namespace Text
{
class Program
{
//下面的实例演示了 sleep() 方法的使用,用于在一个特定的时间暂停线程。
public static void Method()
{
Console.WriteLine("启动线程");
// 线程暂停 5000 毫秒
int t = 5000;
Console.WriteLine("线程暂停 {0} 秒",
t / 1000);
Thread.Sleep(t);
Console.WriteLine("线程恢复");
}
static void Main(string[] args)
{
ThreadStart ts = new ThreadStart(Method);
Thread th = new Thread(ts);//创建线程
th.Start(); //启动线程
Console.ReadKey();
}
}
}
【程序分析】本例演示了线程的休眠。在代码中,首先用户可以自定义一个静态的void方法Method;然后在Main方法中,创建ThreadStart委托的实例ts;接着通过ts构造Thread类的实例th,这样就创建好一个线程;最后通过Start方法,启动线程,由于在Method方法中调用了Sleep方法,使线程休眠5秒后再运行。
启动线程
线程暂停 5 秒
线程恢复
线程的挂起与恢复
Suspend方法用于挂起线程,Resume方法用于继续执行已经挂起的线程。可以使用这两个方法进行线程的同步,和Start方法有些类似的是,在调用Suspend方法后不会立即停止,而是执行到一个安全点后挂起。
- Suspend方法
挂起线程,或者如果线程已挂起,则不起作用,语法格式如下:
public void Suspend();
- Resume方法
继续已挂起的线程,语法格式如下:
public void Resume();
编写程序,自定义两个线程,主线程(MainThread方法)和工作线程(WorkThread方法),主线程倒序输出,工作线程正序输出。先完成主线程,再完成工作线程。
using System;
using System.Threading;
namespace ThreadExample
{
class Program
{
static void Main(string[] args)
{
ThreadStart entry = new ThreadStart(CalcSum);
Thread workThread = new Thread(entry);
workThread.Start();
workThread.Suspend();//挂起线程(改动部分)
CalcGap();
workThread.Resume();//恢复线程(改动部分)
Console.ReadKey();
}
static void CalcSum()
{
for (long i = 0; i < 1000000000; i++)
{
if (i % 100000000 == 0 && i != 0)
{
Console.WriteLine("workThread-->i={0}", i);
}
}
}
static void CalcGap()
{
long gap = 0;
for (long i = 1000000000; i >= 0; i--)
{
gap = i ;
if (i % 100000000 == 0 && i != 0)
{
Console.WriteLine("MainThread-->i={0}:gap={1}", i, gap);
}
}
}
}
}
【程序分析】本例演示了线程的挂起与恢复。在代码中,用户定义了两个方法WorkThread和MainThread。MainThread方法用于倒序输出0~10;WorkThread方法用于正序输出1~9;然后在Main方法中,创建ThreadStart委托的实例work;接着通过work构造Thread类的实例th,这样就创建好一个线程;最后通过Start方法,启动线程。挂起线程使用Suspend方法。线程被挂起后,操作被停止或进入休眠状态。因此,从结果中可以看出,此时主线程正常执行,但是工作线程WorkThread没有被执行。那么要想工作线程能继续执行,就需要使用Resume方法恢复线程。
MainThread-->i=1000000000:gap=1000000000
MainThread-->i=900000000:gap=900000000
MainThread-->i=800000000:gap=800000000
MainThread-->i=700000000:gap=700000000
MainThread-->i=600000000:gap=600000000
MainThread-->i=500000000:gap=500000000
MainThread-->i=400000000:gap=400000000
MainThread-->i=300000000:gap=300000000
MainThread-->i=200000000:gap=200000000
MainThread-->i=100000000:gap=100000000
workThread-->i=100000000
workThread-->i=200000000
workThread-->i=300000000
workThread-->i=400000000
workThread-->i=500000000
workThread-->i=600000000
workThread-->i=700000000
workThread-->i=800000000
workThread-->i=900000000
终止线程
线程的终止是通过Thread类的Abort方法和Join方法来实现的。
- Abort方法
当一个线程执行时间太长时,用户有可能要终止这个线程,这就要使用Abort方法。该方法有两种重载方式:
public void Abort() //终止进程
public void Abort(Object stateInfo)
参数stateInfo是一个对象,包含应用程序特定的信息(如状态),该信息可供正被终止的线程使用
注意:在线程调用Abort方法时,会引发ThreadAbortException异常。如果没有捕获异常,线程将会终止通过。
编写程序,启动线程,while循环5次后终止线程。
using System;
using System.Threading;
namespace Project5
{
class Program
{
static void Main(string[] args)
{
ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
Thread th = new Thread(work);
th.Start(); //启动线程
int i = 0;
while (th.IsAlive)
{
i++;
Thread.Sleep(500);
if (i == 5)
{
th.Abort(); //线程被终止
Console.WriteLine("\r\n线程被终止");
}
}
Console.ReadKey();
}
static void WorkThread()
{
for (long i = 0; i < 1000000000; i++)
{
if (i % 100000000 == 0 && i != 0)
{
Console.WriteLine("工作线程WorkThread-->i={0}", i);
}
}
}
}
}
【程序分析】本例演示了线程的终止。在代码中,用户首先自定义方法WorkThread,用于输出一组数据;然后在Main方法中,创建ThreadStart委托的实例work;接着通过work构造Thread类的实例th,这样就创建好一个线程;最后通过Start方法,启动工作线程。中止线程使用Abort方法。线程被中止,就停止运行,是无法恢复的,因为Windows会永久地删除被中止线程的所有数据。跟挂起工作线程时的结果一样,中止工作线程后,工作线程自然不会被执行。
工作线程WorkThread-->i=100000000
工作线程WorkThread-->i=200000000
线程被终止
- Join方法
Join方法用于等待线程中止,如果后续的处理依赖于另一个已经终止的线程,可以调用Join方法,等待线程中止。该方法有三种重载形式:
public void Join()
public void Join(int millisecondsTimeout)
public void Join(TimeSpan timeout)
参数说明:millisecondsTimeout表示等待线程终止的毫秒数。如果线程已终止,则返回值为true,如果线程经过了millisecondsTimeout指定时间后未终止,返回值为false。timeout表示等待线程终止的时间量TimeSpan。如果线程已终止,则返回值为true,如果线程经过timeout时间量之后未终止,则返回值为false。
编写程序,使用Join方法等待线程终止。
using System;
using System.Threading;
namespace Project
{
class Program
{
static void Main(string[] args)
{
ThreadStart work = new ThreadStart(WorkThread); //创建工作线程
Thread th = new Thread(work);
th.Start(); //启动线程
//th.Join();
th.Join(1000);//等待工作线程中止(修改代码)
MainThread();
}
static void WorkThread()
{
for (long i = 1; i < 1000000000; i++)
{
if (i % 100000000 == 0 && i != 0)
{
Console.WriteLine("工作线程WorkThread-->i={0}", i);
}
}
Console.WriteLine("工作线程执行完毕");
}
static void MainThread()
{
long gap = 0;
for (long i = 1000000000; i >= 0; i--)
{
gap = i - 1;
if (i % 100000000 == 0)
{
Console.WriteLine("主线程 MainThread-->i={0}", i);
}
}
Console.WriteLine("主线程执行完毕");
Console.ReadKey();
}
}
}
【程序分析】本例演示了线程的等待终止。在Main方法中,工作线程调用了Join方法,因此,需待工作线程中止后,主线程才会被执行。Join的其他重载方法可以指定等待的时间期限,超过了这个时间期限,程序也会继续执行。因此,可以将“th.Join();”语句修改为“th.Join(1000);”。
工作线程WorkThread-->i=100000000
工作线程WorkThread-->i=200000000
工作线程WorkThread-->i=300000000
工作线程WorkThread-->i=400000000
工作线程WorkThread-->i=500000000
工作线程WorkThread-->i=600000000
工作线程WorkThread-->i=700000000
工作线程WorkThread-->i=800000000
工作线程WorkThread-->i=900000000
工作线程执行完毕
主线程 MainThread-->i=1000000000
主线程 MainThread-->i=900000000
主线程 MainThread-->i=800000000
主线程 MainThread-->i=700000000
主线程 MainThread-->i=600000000
主线程 MainThread-->i=500000000
主线程 MainThread-->i=400000000
主线程 MainThread-->i=300000000
主线程 MainThread-->i=200000000
主线程 MainThread-->i=100000000
主线程 MainThread-->i=0
主线程执行完毕
主线程 MainThread-->i=1000000000
工作线程WorkThread-->i=100000000
工作线程WorkThread-->i=200000000
主线程 MainThread-->i=900000000
工作线程WorkThread-->i=300000000
主线程 MainThread-->i=800000000
工作线程WorkThread-->i=400000000
主线程 MainThread-->i=700000000
工作线程WorkThread-->i=500000000
主线程 MainThread-->i=600000000
工作线程WorkThread-->i=600000000
主线程 MainThread-->i=500000000
工作线程WorkThread-->i=700000000
主线程 MainThread-->i=400000000
工作线程WorkThread-->i=800000000
主线程 MainThread-->i=300000000
工作线程WorkThread-->i=900000000
工作线程执行完毕
主线程 MainThread-->i=200000000
主线程 MainThread-->i=100000000
主线程 MainThread-->i=0
主线程执行完毕
线程的优先级
线程的优先级可以通过Thread类的Priority属性设置,Priority属性是一个ThreadPriority型枚举,列举了5个优先等级:AboveNormal、BelowNormal、Highest、Lowest、Normal。普通线程的优先级默认为Normal;如果想有更高的优先级,可设置为AboveNormal或Highest;如果想有较低的优先级,可设置为BelowNormal或Lowest。线程优先级值,从高到低按顺序如下表所示。
可以通过调用线程的Priority属性来获取和设置其优先级。Priority属性用来获取或设置一个值,该值指示线程的调度优先级。
编写程序,分别创建两个线程,然后通过设定不同的优先级来显示线程的名称和优先级。
using System;
using System.Threading;
namespace Project7
{
class Program
{
public static void method()
{
//输出线程的名称
Console.WriteLine("线程名称:{0}",Thread.CurrentThread.Name.ToString());
//输出线程的优先级
Console.WriteLine("线程的优先级:{0}",Thread.CurrentThread.Priority.ToString());
}
static void Main(string[] args)
{
ThreadStart ts1 = new ThreadStart(method);
ThreadStart ts2 = new ThreadStart(method);
Thread t1 = new Thread(ts1);
Thread t2 = new Thread(ts2);
//为两个线程命名
t1.Name = "学习C#线程一";
t2.Name = "学习Java线程二";
//指定线程t1的优先级为Highest
t1.Priority = ThreadPriority.Highest;
//启动两个线程
t1.Start();
t2.Start();
Console.ReadKey();
}
}
}
【程序分析】本例演示了线程的优先级。在代码中用户自定义方法method,用于输出线程的名称和优先级;然后在Main方法中,创建两个线程t1和t2,首先使用Name属性为两个线程命名,再使用Priority属性设置线程t1的优先级为Highest,最后启动两个线程。通过最后的输出结果发现,t1的优先级值为Highest,t2的优先级值为Normal,这是因为Normal是线程的默认值。
线程名称:学习C#线程一
线程的优先级:Highest
线程名称:学习Java线程二
线程的优先级:Normal