来源:www.cncfan.com | 2006-8-30 | (有2538人读过)
class InventoryManager //事件的发布者 { //声明代表 public delegate void InventoryChangeEventHander(object source, InventoryChangeEventArgs e); //发布事件,event关键字可将一个代表指向多个处理函数 public event InventoryChangeEventHandler onInventoryChangeHander; public void UpdateInventory(string sku, int change) { if (change == 0) return; InventoryChangeEventArgs e = new InventoryChangeEventArgs(sku, change); //触发事件 if (onInventoryChangeHandler != null) //如果有预订者就触发 onInventoryChangeHandler(this, e); //执行代表指向的处理函数 } }
class InventoryWatcher //事件的预订者 { public InventoryWatcher(InventoryManager mgr) //mgr参数用于联结发布者 { this.inventoryManager = mgr; //预订事件,用 += 调用多个处理函数 mgr.onInventroyChangeHandler += new InventoryManager.InventoryChangeEventHandler(onInventoryChange); //事件处理函数 void onInventroyChange(object source, InventroyChangeEventArgs e) { ... }
InventoryManager inventoryManager; } }
class EventsApp //主程序 { public static void Main() { InventoryManager inventoryManager = new InventoryManager(); InventoryWatcher inventoryWatcher = new InventoryWatcher(inventoryManager);
inventoryManager.UpdateInventory("111 006 116", -2); inventoryManager.UpdateInventory("111 006 116", 5); } }
Microsoft Windows NT和IBM OS/2等操作系统都支持占先型多任务。在占先型多任务执行中,处理器负责 给每个线程分配一定量的运行时间——一个时间片(timeslice)。处理器接着在不同的线程之间进行切换, 执行相应的处理。在单处理器的计算机上,并不能真正实现多个线程的同时运行,除非运行在多个处理器 的计算机上。操作系统调度的多线程只是根据分配给每个线程时间片进行切换执行,感觉上就像同时执行。
上下文切换(context switching)是线程运行的一部分,处理器使用一个硬件时间来判断一个指定线程的时间片 何时结束。当这个硬件计时器给出中断信号时,处理器把当前运行的线程所用的所有寄存器(registers)数据 存储到堆栈中。然后,处理器把堆栈里那些相同的寄存器信息存放到一种被称为“上下文结构”的数据结构中。 当处理器要切换回原来执行的线程时,它反向执行这个过程,利用与该线程相关的上下文结构,在寄存器里 重新恢复与这一线程相关的信息。这样的一个完整过程称为“上下文切换”。
多线程允许应用程序把任务分割为多个线程,它们彼此之间可以独立地工作,最大限度地利用了处理器时间。
using System; using System.Threading;
class SimpleThreadApp { public static void WorkerThreadMethod() //线程的执行体 { ... //执行一些操作 }
public static void Main() { //创建一个线程代表指向线程的执行体,ThreadStart是创建新线程必须用到的代表 ThreadStart worker = new ThreadStart(WorkerThreadMethod); Thread t = new Thread(worker); //用线程代表创建线程 t.Start(); //执行线程 } }
可以通过两种方式来得到一个Thread对象:一种是通过创建一个新线程来得到,如上例;另一种在正在执行的线程调用 静态的Thread.CurrentThread方法。 静态方法Thread.Sleep(int ms)可以让当前线程(它自动调用Thread.CurrentThread)暂停指定毫秒的时间。 如果使用Thread.Sleep(0)那么当前线程将一直处于等待中,直到另一个线程调用这个线程的实例方法Thread.Interrupt方法, 等待才会结束。 使用Thread.Suspend方法也能挂起线程,Thread.Suspend方法可以被当前线程或其他线程调用,而Thread.Sleep(0) 只能由当前线程在执行体中调用。当线程用Thread.Suspend挂起时,必须用Thread.Resume方法恢复。不论Thread.Suspend 方法调用了多少次,只要调用Thread.Resume方法一次就可以线程恢复执行。用Thread.Suspend方法并不会阻塞线程, 调用立即返回。而Thread.Sleep(0)则会阻塞线程。所以确切地说Thread.Sleep(0)暂停线程,而不是挂起线程。 使用Thread.Abort方法可以终止正在执行的线程。当Thread.Abort方法被调用时,线程不会立即终止执行。运行环境将会 等待,直到线程到达文档中所描述的“安全点”。如果要确保线程已经完全停止,可以使用Thread.Join方法。这是一个同步 调用,同步调用意味着直到线程完全停止,调用才会返回。 Thread.Priority属性用于设置的线程的优先级。其值是Thread.ThreadPriority枚举值,可以设为Highest, AboveNormal, Normal, BelowNormal, Lowest。缺省值是Thread.ThreadPriority.Normal。 线程的同步是为了解决多个线程同时使用同一对象产生的一些问题。通过同步,可以指定代码的临界区(critical section), 一次只有一个线程可以进入临界区。 使用System.Monitor类(锁定与信号量)进行线程同步: using System; using System.Threading;
public void SaveData(string text) //线程执行函数或线程执行函数调用的对象的方法 { ... //执行其他一些不需要同步的处理
Monitor.Enter(this); //获取对象的Monitor锁 ... //执行需要同步的处理 Monitor.Exit(this); //释放对象的Monitor锁
... //执行其他一些不需要同步的处理 } 说明:当执行Monitor.Enter方法时。这个方法会试图获取对象上的Monitor锁,如果另一个线程已经拥有了 这个锁,这个方法将会阻塞(block),直到这个锁被释放。 也可用C#的lock语句来获得和释放一个Monitor锁。上面同步写成: public void SaveData(string text) //线程执行函数或线程执行函数调用的对象的方法 { ... //执行其他一些不需要同步的处理
lock(this) //获取对象的Monitor锁,代码块执行完成后释放Monitor锁 { ... //执行需要同步的处理 }
... //执行其他一些不需要同步的处理 }
也可以使用System.Threading名称空间的Mutex类(互斥类)进行线程同步。与Monitor锁一样,一次只有一个线程 能获得一个给定的互斥。但Mutex要慢得多,但它增加了灵活性。例: using System; using System.Threading; class Database { Mutex mutex = new Mutex(false); //创建一个互斥,但不立即获得它 //注意:创建互斥在需要同步的方法之外,实际上它只要创建一个实例 public void SaveData(string text) //需要同步的方法 { mutex.WaitOne(); //等待获得互斥 ... //需要同步的处理 mntex.Close(); //释放互斥 } }
Mutex类重载了三个构造函数: Mutex() //创建并使创建类立即获得互斥 Mutex(bool initiallyOwned) //创建时可指定是否要立即获得互斥 Mutex(bool initiallyOwned, string muterName) //还可以指定互斥的名称 Mutex.WaitOne方法也重载了三次: Mutex.WaitOne() //一直等待 Mutex.WaitOne(TimeSpan time, bool exitContext) //等待TimeSpan指定的时间 Mutex.WaitOne(int milliseconds, bool exitContext) //等待指定的毫秒 线程的用法: 1)并发操作:比如一个程序监视多个COM口,当每个COM接到信息时执行一段处理时。 2)复杂长时间操作:一个长时间的复杂操作可能会使界面停滞,停止用户响应,如果还允许用户停止它, 或者显示进度条、显示操作执行进程信息时。 反射(Reflection)就是能够在运行时查找类型信息,这是因为.NET编译的可执行(PE)文件中包括MSIL和元数据(metadata)。 反射的中心是类System.Type。System.Type是一个抽象类,代表公用类型系统(Common Type System, CTS)中的一种类型。 using System; using System.Reflection; //反射命名空间,必须引用
public static void Main(string[] args) { int i = 6; Type t = i.GetType(); //根据实例得到类型 t = Type.GetType("System.Int32"); //根据类型的字符名称得到类型 }
通过Assembly类可以得到已经编译.NET Framework程序的中所有类型,例: using System; using System.Diagnostics; //为了使用Process类 using System.Reflection; //为了使用Assembly类
class GetTypesApp { protected static string GetAssemblyName(string[] args) { string assemblyName; if (0 == args.Length) //如果参数为空,取当前进程的名称 { Process p = Process.GetCurrentProcess(); assemblyName = p.ProcessName + ".exe"; } else assemblyName = args[0]; //取第一个参数,即当前运行程序名
return assemblyName; }
public static void Main(string[] args) { string assemblyName = GetAssemblyName(args); Assembly a = Assembly.LoadFrom(assemblyName); //调用编译程序集 Type[] types = a.GetTypes(); //得到多个类型 foreach (Type t in types) //遍历类型数组 { ... //取得t.FullName,t.BaseType.FullName等类型信息 } } } 一个应用程序可以包括多个代码模块。若要将一个cs文件编译一个模块,只要执行下面的命令: csc /target:module 要编译的模块.cs //csc是C Sharp Compiler(C#编译器) 然后在应用程序中using编译的模块.cs中的NameSpace即可应用了。 要反射应用程序中所有代码模块(Module),只要: Assembly a = Assembly.LoadFrom(assemblyName); //应用程序的物理文件名 Module[] modules = a.GetModules(); foreach(Module m in modules) { ... //显示m.Name等 } 后期绑定(latebinding),例: string[] fileNames = Directory.GetFiles(Environment.CurrentDirectory, "*.dll"); foreach (string fileName in fileNames) { Assembly a = Assembly.LoadFrom(fileName); Type[] types = a.GetTypes(); foreach(Type t in types) { if (t.IsSubclassOf(typeof(CommProtocol))) //判断是否有CommProtocol的派生类 { object o = Activator.CreateInstance(t); //生成实例 MethodInfo mi = t.GetMethod("DisplayName"); mi.Invoke(o, null); //调用方法 } } } //带参数的例子 namespace Programming_CSharp { using System; using System.Reflection; public class Tester { public static void Main( ) { Type t = Type.GetType("System.Math"); Object o = Activator.CreateInstance(t);
// 定义参数类型 Type[] paramTypes = new Type[1]; paramTypes[0]= Type.GetType("System.Double");
MethodInfo CosineInfo = t.GetMethod("Cos", paramTypes);
//设置参数数据 Object[] parameters = new Object[1]; parameters[0] = 45;
//执行方法 Object returnVal = CosineInfo.Invoke(o, parameters); Console.WriteLine("The cosine of a 45 degree angle {0}", returnVal); } } } 动态生成代码和动态调用的完整例子: //动态生成代码的部分 using System; using System.Reflection; using System.Reflection.Emit; //动态生成代码必须引用
namespace ILGenServer { public class CodeGenerator { public CodeGenerator() { currentDomain = AppDomain.CurrentDomain; //得到当前域 assemblyName = new AssemblyName(); //从域创建一个程序集 assemblyName.Name = "TempAssembly"; //得到一个动态编译生成器,AssemblyBuilerAccess.Run表示只在内存中运行,不能保存 assemblyBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilerAccess.Run); //从编译生成器得到一个模块生成器 moduleBuilder = assemblyBuilder.DefineDynamicModule("TempModule"); //模块生成器得到类生成器 typeBuilder = moduleBuilder.DefineType("TempClass", TypeAttributes.Public); //为类添加一个方法 methodBuilder = typeBuilder.DefineMethod("HelloWord", MethodAttributes.Public, null, null); //为方法写入代码,生成代码必须使用到IL生成器 msil = methodBuilder.GetILGenerator(); msil.EmitWriteLine("Hello World"); msil.Emit(OpCodes.Ret); //最后还需要编译(build)一下类 t = typeBuilder.CreateType(); } AppDomain currentDomain; AssemblyName assemblyName; AssemblyBuilder assemblyBuilder; ModuleBuilder moduleBuilder; TypeBuilder typeBuilder; MethodBuilder methodBuilder; ILGenerator msil; object o; Type t; public Type T { get { return this.t; } } } } //动态调用的部分 using System; using System.Reflection; using ILGenServer; //引用动态生成代码的类 public class ILGenClientApp { public static void Main( { CodeGenerator gen = new CodeGenerator(); //创建动态生成类 Type t = gen.T; if (null != t) { object o = Activator.CreateInstance(t); MethodInfo helloWorld = t.GetMethod("HelloWorld"); //为调用方法创建一个MethodInfo if (null != helloWorld) { helloWorld.Invoke(o, null); //调用方法 } } } } 调用DLL using System; using System.Runtime.InteropServices; //为了使用DLLImport特性
|