attributes && reflections &&Thread&&Synchronization
1.属性和反射
属性是对目标元素的相关数据的代表。
同时C# 还具有一个反射系统,可用来检索用自定义属性定义的信息。(否则自定义属性没什么意义,不过老师说可以用一些软件来查看,比如书上说的:ILDasm)
老师讲的比较快,听得不是很清楚,后来翻了翻书,看到“自定义属性”的内容,进行了尝试。
这个例子是把程序的错误信息绑定到代码的特定修正中(特定修正?),然后利用反射将元数据输出到console流里。
1 namespace CustomAttributes 2 { 3 //create attributes 4 [AttributeUsage(AttributeTargets.Class|AttributeTargets.Constructor|AttributeTargets.Field 5 |AttributeTargets.Method|AttributeTargets.Property,AllowMultiple = true)] 6 public class BugFixAttribute : System.Attribute 7 { 8 //private class member 9 private int bugID; 10 private string comment; 11 private string date; 12 private string programmer; 13 //按声明顺序导入参数 构造函数 14 public BugFixAttribute(int bugID, string programmer, string date) 15 { 16 this.bugID = bugID; 17 this.programmer = programmer; 18 this.date = date; 19 } 20 //只读参数访问器 21 public int BugID 22 { 23 get { return bugID; } 24 } 25 //带名参数的特征 26 public string Comment 27 { 28 get { return comment; } 29 set { comment = value; } 30 } 31 public string Date 32 { 33 get { return date; } 34 } 35 public string Programmer 36 { 37 get { return programmer; } 38 } 39 40 } 41 [BugFixAttribute(121,"mark","15/04/30")] 42 [BugFixAttribute(107,"mark","15/05/02",Comment= "fixed off by one error")] 43 44 public class MyMath 45 { 46 public double f1(double p1) 47 { 48 return p1 + f2(p1); 49 } 50 public double f2(double p1) 51 { 52 return p1 / 3; 53 } 54 } 55 public class Tester 56 { 57 public static void Main () 58 { 59 MyMath mm = new MyMath(); 60 Console.WriteLine("call f(7).result:{0}", mm.f1(7)); 61 62 //获得成员信息并检索自定义属性 63 System.Reflection.MemberInfo inf = typeof(MyMath); 64 object[] attributes; 65 attributes = inf.GetCustomAttributes(typeof(BugFixAttribute), false); 66 67 //遍历属性并检索 68 foreach( Object attribute in attributes) 69 { 70 BugFixAttribute bfa = (BugFixAttribute)attribute; 71 Console.WriteLine("\nBugID:{0},bfa {0}", bfa.BugID); 72 Console.WriteLine("Programmer: {0}", bfa.Programmer); 73 Console.WriteLine("Date: {0}", bfa.Date); 74 Console.WriteLine("Comment: {0}", bfa.Comment); 75 } 76 } 77 } 78 }
结果为:
其中代码的后半段的反射所使用的主要方法是 GetCustomAttributes,它返回对象数组,这些对象在运行时等效于源代码属性。
2.线程与同步
1)创建线程
线程使程序在同一时间内做不同的事情。
多处理器:快(同时进行)
单处理器:慢(交替进行),来回切换
下面创建了2个线程,分别从0数到1000和从1000数到0
1 public static void Main () 2 { 3 Tester t = new Tester(); 4 Console.WriteLine("let us begin the threading testing!"); 5 t.DoTest(); 6 7 } 8 public void DoTest() 9 { 10 Thread t1 = new Thread(new ThreadStart(f1)); 11 Thread t2 = new Thread(new ThreadStart(f2)); 12 t1.Start(); 13 t2.Start(); 14 } 15 public void f1() 16 { 17 for(int i = 0; i < 1000; i++) 18 { 19 System.Console.WriteLine("f1:{0}", i); 20 } 21 } 22 public void f2() 23 { 24 for (int i = 1000; i > 0; i--) 25 { 26 System.Console.WriteLine("f2:{0}", i); 27 } 28 }
结果为:可知,两个线程是交替进行的。
2)拼接线程
把t2.Join();写入t1的一个函数里,运行时t1将会挂起等待知道t2结束。
3)通过睡眠阻塞线程
加Thread.Sleep(),会让线程有机会在另一个线程打印一个值的时候运行。例如将1)中加入sleep,则结果变为:
4)中断线程
推荐的方法:设置布尔标记KeepAlive,线程间隔地检测这个值,当标记改变状态时,线程可以自己停止自己。
(这是百度到的教程里的代码和讲解,感觉比课本里的清晰,但是好像用的是interrupt课本的略乱)
代码中声明了两个线程,创建两线程的委托实例,例子是输出从1到50的整数,并在输出10,20和30之后使线程睡眠;输出从51到99的整数,并自动检测线程sleeper的状态,当sleeper处于WaitSleepJoin时,调用Interrupt使其回到调度队列:
1 public class SleepAndInterrupt 2 3 { 4 //声明两线程 5 public static Thread sleeper; 6 public static Thread interrupter; 7 public static void Main() 8 { 9 Console.WriteLine("进入Main"); 10 //创建两线程的委托实例 11 sleeper = new Thread(new ThreadStart(SleepThread)); 12 interrupter = new Thread(new ThreadStart(InterruptThread)); 13 //开始执行两线程 14 sleeper.Start(); 15 interrupter.Start(); 16 Console.WriteLine("退出Main"); 17 } 18 //输出从1到50的整数,并在输出10,20和30之后使线程睡眠 19 public static void SleepThread() 20 { 21 for(int i = 1; i <= 50; i++) 22 { 23 Console.Write (i+" "); 24 if(i == 10 || i == 20 || i == 30) 25 { 26 Console.WriteLine("在{0}处进入睡眠", i); 27 //捕捉使线程睡眠抛出的异常 28 try 29 { 30 Thread.Sleep(10); 31 } 32 catch 33 { 34 } 35 } 36 } 37 } 38 //输出从51到99的整数,并自动检测线程sleeper的状态 39 //当sleeper处于WaitSleepJoin时,调用Interrupt使其回到调度队列 40 public static void InterruptThread() 41 { 42 for(int i = 51; i < 100; i++) 43 { 44 Console.Write(i+" "); 45 //判断线程sleeper的当前状态 46 if(sleeper.ThreadState == System.Threading.ThreadState.WaitSleepJoin) 47 { 48 Console.WriteLine("中断睡眠中的线程"); 49 sleeper.Interrupt(); 50 } 51 } 52 } 53 }
结果为:
5)同步
同步通过对对象加锁实现,帮助程序员阻止另一个线程闯入你的对象,知道第一个线程不再使用这个对象为止。
同步有三种方式:互锁类,c#lock语句,监控器器对象。
首先模拟一个共享资源:
1 namespace SharedResource 2 { 3 class Tester 4 { 5 private int counter = 0; 6 static void Main() 7 { 8 Tester t = new Tester(); 9 t.DoTest(); 10 } 11 public void DoTest() 12 { 13 Thread t1 = new Thread(new ThreadStart(f1)); 14 t1.IsBackground = true; 15 t1.Name = "threadOne"; 16 t1.Start(); 17 Console.WriteLine("Started thread {0}", t1.Name); 18 Thread t2 = new Thread(new ThreadStart(f1)); 19 t2.IsBackground = true; 20 t2.Name = "threadOne"; 21 t2.Start(); 22 Console.WriteLine("Started thread {0}", t2.Name); 23 24 t1.Join(); 25 t2.Join(); 26 27 //所有线程结束后打印一个消息 28 Console.WriteLine("ALL thread are done."); 29 } 30 public void f1() 31 { 32 try 33 { 34 while(counter < 1000) 35 { 36 int temp = counter; 37 temp++; 38 Thread.Sleep(1); 39 40 counter = temp; 41 Console.WriteLine("Thread {0}.f1:{1}", Thread.CurrentThread.Name, counter); 42 } 43 } 44 catch (ThreadInterruptedException) 45 { 46 Console.WriteLine("thread{0} interrupted! Cleaning up...", Thread.CurrentThread.Name); 47 48 } 49 finally 50 { 51 Console.WriteLine("Thread{0} existing.", Thread.CurrentThread.Name); 52 } 53 } 54 55 } 56 }
结果为:(部分)
然后使用互锁(Interlocked)
改变上文中方法的try语句
1 try 2 { 3 while(counter < 1000) 4 { 5 int temp = Interlocked.Increment(ref counter); 6 Thread.Sleep(0); 7 Console.WriteLine("Thread {0}.f1:{1}", Thread.CurrentThread.Name, temp); 8 } 9 }
结果为(部分):
此时对成员counter的访问就是同步的。
然后是更广泛使用的同步机制c#lock,其可以控制对其他对象的访问,其标记了代码中的一个临界区,当锁有效的时候对指定的对象提供同步机制。
将代码中try改为:
1 try 2 { 3 while(counter < 1000) 4 { 5 int temp; 6 Thread.Sleep(0); 7 lock(this) 8 { 9 temp = counter; 10 temp++; 11 Thread.Sleep(1); 12 counter = temp; 13 } 14 Console.WriteLine("Thread {0}.f1:{1}", Thread.CurrentThread.Name, temp); 15 } 16 }
输出与互锁相同。
over