C# 线程手册 第二章 .net 中的线程 创建一个线程
C# 线程手册 第二章 .net 中的线程 创建一个线程
2012年01月12日
我们将写一个简单的例子。对于我们为什么使用一个新的线程来说这不是一个好例子但是它将我们稍后要提到的复杂问题都去掉了。创建一个simple_thread.cs文件并把下面的代码粘贴进去: using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace SimpleThread { class SimpleThread { void SimpleMethod() { int i = 5; int x = 10; int result = i * x; Console.WriteLine("This code calculated the value " + result.ToString() + " from thread ID: " + Thread.CurrentThread.ManagedThreadId); } static void Main(string[] args) { //Calling the method from our current thread. SimpleThread simpleThread = new SimpleThread(); simpleThread.SimpleMethod(); //Calling the method on a new thread. ThreadStart ts = new ThreadStart(simpleThread.SimpleMethod); Thread t = new Thread(ts); t.Start(); Console.ReadLine(); } } }
现在保存,编译然后运行。你的输出会类似下面的结果:
我们继续研究这个小例子以确定我们确实了解发生了什么。由于我们已经知道线程相关功能都被封装到System.Threading命名空间中去了。所以我们必须首先将这个命名空间导入到我们的工程。一旦命名空间导入了,我们就可以在主线程中和新的工作线程中创建一个方法。我们使用SimpleMethod()方法: void SimpleMethod() { int i = 5; int x = 10; int result = i * x; Console.WriteLine("This code calculated the value " + result.ToString() + " from thread ID: " + Thread.CurrentThread.ManagedThreadId); }
我们使用第一章介绍的AppDomain来找出正在运行哪个线程。这个方法不论何时执行,都显示一个哪个线程在执行操作的报告。
我们的程序入口是Main()方法。首先会调用与主方法运行在同一个线程的SimpleMethod()方法。下一步很重要:我们第一次可以看看如何创建一个线程。在C#中创建一个线程之前,我们首先必须创建一个ThreadStart委托实例。委托是一个面向对象的类型安全的程序指针。由于我们将要告诉一个线程执行什么方法,所以我们必须将程序指针传递给线程的构造函数。我们的程序中会描述这个: ThreadStart ts = new ThreadStart(simpleThread.SimpleMethod);
需要注意这里的方法名是不加括号的;它只是简单的使用方法名。当我们创建完ThreadStart委托,我们接下来可以创建我们的线程来执行代码。Thread唯一的构造函数将ThreadStart委托的实例作为参数。我们来看看代码中是如何表示的: Thread t = new Thread(ts);
我们定义了一个变量t作为新线程的名字。线程类的构造函数将ThreadStart委托作为它唯一的参数。
下一行代码是Thread对象的Start()方法。通过调用我们传给构造函数的ThreadStart委托,我们开始一个新执行线程。最后通过Console.ReadLine()来等待我们的键盘输入: t.Start(); Console.ReadLine();
额,我们已经创建了一个线程,但是仅仅这些还不能帮助我们洞悉线程的力量。事实上除了显示不同的线程ID我们还没有做别的。为了在一个更加真实的应用场景中看一下如何使用同样的线程代码,我们将创建另外一个模拟后台有一个长时间运行线程同时前台有另外一个线程的程序。在一个新文件do_something_thread.cs中创建一个新的控制台应用程序: using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace SimpleThread { public class DoSomethingThread { static void WorkerMethod() { for (int i = 1; i
由于上面的这段代码并没有介绍新技术所以咱们就略过。然而,需要注意在两个线程在分享执行时间。任意一个线程都会被阻塞直到另外一个执行完。每个线程都被分配一个很短的时间来执行。在一个线程用完自己的执行时间后,下一个线程会在自己的时间片内开始执行。两个线程互相交替直到执行结束。事实上,不是只有我们的两个线程在交替并共享时间片。我们不是仅在我们的程序中交换时间片。现实是,我们与当前计算机中运行的很多线程共享执行时间。
ThreadStart 和 执行分支 我们再看一下之前提到的ThreadStart委托。我们可以使用这些委托做一些有意思的事情。举个现实世界中的例子。假设你想在一个用户运行某个程序时做一些后台的例行任务。不同的角色运行同样程序会在后台执行不同的例行任务。例如,假设一个管理员运行了一个程序,你想在后台运行一个线程来收集报告数据并对数据进行格式化处理(做一个报表)。后台线程会在报表准备好的时候通知管理员。你可能不想对普通用户也提供类似管理员的这种报表服务。这就是ThreadStart的面向对象功能有用的地方。
现在看一些简单的例子。我们不会准确地模拟上面描述的场景,但是我们将演示如何依据ThreadStart定义的特定标准处理不同情况。创建一个应用程序以及一个新文件ThreadStartBranching.cs, 然后把下面代码复制到新文件中: using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace SimpleThread { public class ThreadStartBranching { enum UserClass { ClassAdmin, ClassUser, } static void AdminMethod() { Console.WriteLine("Admin Method"); } static void UserMethod() { Console.WriteLine("User Method"); } static void ExecuteFor(UserClass uc) { ThreadStart ts; ThreadStart tsAdmin = new ThreadStart(AdminMethod); ThreadStart tsUser = new ThreadStart(UserMethod); if (uc == UserClass.ClassAdmin) { ts = tsAdmin; } else { ts = tsUser; } Thread t = new Thread(ts); t.Start(); } static void Main() { //execute in the context of an admin user ExecuteFor(UserClass.ClassAdmin); //execute in the context of a regular user ExecuteFor(UserClass.ClassUser); Console.ReadLine(); } } }
代码的输出结果:
我们将描述从上面代码中得到的一些重点。首先,你将注意到我们创建了可能执行程序的用户集合: enum UserClass { ClassAdmin, ClassUser, }
接下来我们创建了两个方法:AdminMethod() 和 UserMethod(). 这两个方法会执行一系列指令并依据类型的不同而得到不同结果。我们这里只想确定它们已经运行了所以就简单地把结果输出到控制台: static void AdminM