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

请按任意键继续……