25.5 多线程编程
从本节开始涉及多线程编程的一些知识,例如Thread类的使用、线程池、锁,等等。多线程编程的好处前文已经说过了,不再赘述。在开始前,先了解一下多线程编程有哪些不利的地方,目的是让读者知道,多线程编程也不能滥用,要结合实际情况斟酌使用。
❑线程也是对象,也需要占用内存,因此线程越多占用内存也就越多;
❑多线程需要协调和管理,所以需要CPU时间跟踪线程;
❑线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
❑线程太多会导致控制太复杂,最终可能带来产生Bug的风险。
25.5.1 Thread类
使用Thread类可以创建一个线程,它位于System.Threading命名空间。我们来看看Thread有哪些构造函数:
❑public Thread(ParameterizedThreadStart start)
❑public Thread(ThreadStart start)
❑public Thread(ParameterizedThreadStart start,int maxStackSize)
❑public Thread(ThreadStart start,int maxStackSize)
这些构造函数涉及3种类型的参数。
1)ParameterizedThreadStart:是一个委托类型,表示此线程开始执行时要调用的方法,支持向调用的方法传递一个参数。该委托的签名如下:
public delegate void ParameterizedThreadStart(object obj);
2)ThreadStart:是一个委托类型,表示此线程开始执行时要调用的方法没有参数的委托类型,不支持向调用的方法传递一个参数。该委托的签名如下:
public delegate void ThreadStart();
3)maxStackSize(int类型):表示线程要使用的最大堆栈大小,如果为0则使用可执行文件的文件头中指定的默认最大堆栈大小。从.NET Framework 4.0开始,只有完全受信任的代码可以将此值设置为大于默认堆栈大小(1MB)的值。如果在部分信任的情况下运行代码时为此值指定更大的值,则maxStackSize将被忽略,并且使用默认堆栈大小,这并不会引发异常。任何信任级别的代码可以将maxStackSize设置为小于默认堆栈大小的值。
接下来使用代码来说明Thread类的基本使用方法,在示例代码中分别演示了ThreadStart和ParameterizedThreadStart两个委托的使用方法。需要注意两点:其一是Thread类的实例只有调用Start方法线程才可以执行;其二是使用ThreadStart和ParameterizedThreadStart两个委托时,使用的也分别是两个不同的Start方法重载,详情如代码清单25-8所示。
代码清单25-8 Thread类基本使用的示例代码
using System;
using System.Threading;
namespace ProgrammingCSharp4
{
class AsynchronousSample
{
public static void Main()
{
Console.WriteLine(“[主线程id:{0}]”,Thread.CurrentThread.ManagedThreadId);
Thread thread1=new Thread(Print);
thread1.Start();
Thread thread2=new Thread(PrintEx);
thread2.Start("test");
}
public static void Print()
{
int threadId=Thread.CurrentThread.ManagedThreadId;
Console.WriteLine(“[当前线程id:{0}]{1}”,threadId,DateTime.Now.ToShortTimeString());
}
public static void PrintEx(object content)
{
int threadId=Thread.CurrentThread.ManagedThreadId;
Console.WriteLine(“[当前线程id:{0}]\t{1}”,threadId,content);
}
}
}
上述代码的运行结果为:
[主线程id:1]
[当前线程id:4]test
[当前线程id:3]16:47
请按任意键继续……