1. 上篇
C# 泛型 (OK)
C# 匿名方法 (OK)
C# 委托 (OK)
C# 集合 (OK)
C# 索引器 (OK)
C# 事件 (OK)
C# 属性 (OK)
2. 下篇
C# 特性
C# 反射
C# 不安全代码
C# 多线程
C# 特性
由 小路依依 创建, 最后一次修改 2016-08-12
特性
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
列举特性
列举特性的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性的名称和值都是在方括号内规定的,放在这个特性应用的元素之前。表示位置的参数规定出特性的基本信息,名称参数规定了可选择的信息。
预定义特性
.Net Framework 提供了三种预定义的特性:
- AttributeUsage
- Conditional
- Obsolete
AttributeUsage
该特性描述了用户定义的特性类是如何使用的。它规定了某个特性应用的项目类型。
规定这种特性的语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
其中,
- 参数 validon 规定特性了能承载特性的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
- 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性提供了一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
- 参数 inherited(可选的)为该特性的 Inherited 属性提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不可继承)。
例如:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
Conditional
这个预定义特性标记了一个条件方法,其执行依赖于特定的预处理标识符。
它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。
规定该特性的语法如下:
[Conditional(
conditionalSymbol
)]
例如:
[Conditional("DEBUG")]
下面的实例演示了该特性:
#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
编译执行上述代码,得到如下结果:
In Main function
In Function 1
In Function 2
Obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
规定该特性的语法如下:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
其中,
- 参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。
- 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
下面的实例演示了该特性:
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
当你执行这个程序时,编译器会提示如下的错误:
Don't use OldMethod, use NewMethod instead
创建自定义的特性
.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。
声明自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
//一个自定义的特性BugFix被分配给类和类的成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
在上面的代码中,我们已经声明了一个名为 DeBugInfo 的自定义特性。
构建自定义特性
让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:
- bug 的代码编号
- 辨认该 bug 的开发人员名字
- 最后一次审查该代码的日期
- 一个存储了开发人员标记的字符串消息
我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。
每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:
//一个自定义的特性BugFix被分配给类和类的成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
应用自定义特性
通过将特性放置在目标之前来使用它:
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
//成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
在下一章节中,我们会介绍如何使用 Reflection 类来检索特性信息。
C# 反射
由 小路依依 创建, 最后一次修改 2016-08-12
反射
反射(Reflection) 对象用于在运行时获取类型信息。该类位于 System.Reflection 命名空间中,可访问一个正在运行的程序的元数据。
System.Reflection 命名空间包含了允许您获取有关应用程序信息及向应用程序动态添加类型、值和对象的类。
反射的应用
反射(Reflection)有下列用途:
- 它允许在运行时查看属性(attribute)信息。
- 它允许审查集合中的各种类型,以及实例化这些类型。
- 它允许延迟绑定的方法和属性(property)。
- 它允许在运行时创建新类型,然后使用这些类型执行一些任务。
查看元数据
我们已经在上面的章节中提到过,使用反射(Reflection)可以查看属性(attribute)信息。
System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的属性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下:
System.Reflection.MemberInfo info = typeof(MyClass);
下面的程序示范了这点:
using System;
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute
{
public readonly string Url;
public string Topic // Topic 是一个表示名字的参数
{
get
{
return topic;
}
set
{
topic = value;
}
}
public HelpAttribute(string url) // url 是一个表示位置的参数
{
this.Url = url;
}
private string topic;
}
[HelpAttribute("Information on the class MyClass")]
class MyClass
{
}
namespace AttributeAppl
{
class Program
{
static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
System.Console.WriteLine(attributes[i]);
}
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义属性:
HelpAttribute
示例
在本实例中,我们将使用在上一章中创建的 DeBugInfo 属性,并使用反射(Reflection)来读取 Rectangle 类中的元数据。
using System;
using System.Reflection;
namespace BugFixApplication
{
//自定义特性BugFix分配给一个类和他的成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
//成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}//Rectangle 类的结束
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(4.5, 7.5);
r.Display();
Type type = typeof(Rectangle);
//遍历 Rectangle 类的属性
foreach (Object attributes in type.GetCustomAttributes(false))
{
DeBugInfo dbi = (DeBugInfo)attributes;
if (null != dbi)
{
Console.WriteLine("Bug no: {0}", dbi.BugNo);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
//遍历方法属性
foreach (MethodInfo m in type.GetMethods())
{
foreach (Attribute a in m.GetCustomAttributes(true))
{
DeBugInfo dbi = (DeBugInfo)a;
if (null != dbi)
{
Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}", dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
}
Console.ReadLine();
}
}
}
编译执行上述代码,得到如下结果:
Length: 4.5
Width: 7.5
Area: 33.75
Bug No: 49
Developer: Nuha Ali
Last Reviewed: 10/10/2012
Remarks: Unused variable
Bug No: 45
Developer: Zara Ali
Last Reviewed: 12/8/2012
Remarks: Return type mismatch
Bug No: 55, for Method: GetArea
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks: Return type mismatch
Bug No: 56, for Method: Display
Developer: Zara Ali
Last Reviewed: 19/10/2012
Remarks:
C# 不安全代码
由 小路依依 创建, 最后一次修改 2016-08-12
不安全代码
当代码段被 unsafe 修饰符标记时,C# 允许该代码段中的函数使用指针变量,故使用了指针变量的代码块又被称为不安全代码或非托管代码。
注意:若要在 codingground 中执行本章的程序,请将 Project >> Compile Options >> Compilation Command to 中的编辑项设置为 mcs *.cs -out:main.exe -unsafe"
指针
指针是指其值为另一个变量的地址的变量,即内存位置的直接地址。如一般的变量或常量一样,在使用指针来存储其他变量的地址之前,必须先进行指针声明。
声明指针变量的一般形式为:
type *var-name;
以下为一些有效的指针声明示例:
int *ip; /* pointer to an integer */
double *dp; /* pointer to a double */
float *fp; /* pointer to a float */
char *ch /* pointer to a character */
下述示例为在 C# 中使用了 unsafe 修饰符时指针的使用:
using System;
namespace UnsafeCodeApplication
{
class Program
{
static unsafe void Main(string[] args)
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} ", var);
Console.WriteLine("Address is: {0}", (int)p);
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果:
Data is: 20
Address is: 99215364
除了将整个方法声明为不安全代码之外,还可以只将部分代码声明为不安全代码,下面章节中的的示例说明了这点。
使用指针检索数据值
使用 ToString() 方法可以检索存储在指针变量所引用位置的数据。例如:
using System;
namespace UnsafeCodeApplication
{
class Program
{
public static void Main()
{
unsafe
{
int var = 20;
int* p = &var;
Console.WriteLine("Data is: {0} " , var);
Console.WriteLine("Data is: {0} " , p->ToString());
Console.WriteLine("Address is: {0} " , (int)p);
}
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果
Data is: 20
Data is: 20
Address is: 77128984
传递指针作为方法的参数
指针可以作为方法中的参数,例如:
using System;
namespace UnsafeCodeApplication
{
class TestPointer
{
public unsafe void swap(int* p, int *q)
{
int temp = *p;
*p = *q;
*q = temp;
}
public unsafe static void Main()
{
TestPointer p = new TestPointer();
int var1 = 10;
int var2 = 20;
int* x = &var1;
int* y = &var2;
Console.WriteLine("Before Swap: var1:{0}, var2: {1}", var1, var2);
p.swap(x, y);
Console.WriteLine("After Swap: var1:{0}, var2: {1}", var1, var2);
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果:
Before Swap: var1: 10, var2: 20
After Swap: var1: 20, var2: 10
使用指针访问数组元素
在 C# 中,数组名称与一个指向与数组数据具有相同数据类型的指针是不同的变量类型。例如 int *p 和 int[] 是不同的类型。你可以对指针变量 p 进行加操作,因为它在内存中是不固定的,反之,数组的地址在内存中是固定的,因而无法对其直接进行加操作。
故如同 C 或 C++,这里同样需要使用一个指针变量来访问数组数据,并且需要使用 fixed 关键字来固定指针,示例如下:
using System;
namespace UnsafeCodeApplication
{
class TestPointer
{
public unsafe static void Main()
{
int[] list = {10, 100, 200};
fixed(int *ptr = list)
/* let us have array address in pointer */
for ( int i = 0; i < 3; i++)
{
Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i));
Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i));
}
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果:
Address of list[0] = 31627168
Value of list[0] = 10
Address of list[1] = 31627172
Value of list[1] = 100
Address of list[2] = 31627176
Value of list[2] = 200
编译不安全代码
必须切换命令行编译器到指定的 /unsafe 命令行才能进行不安全代码的编译。
例如,编译一个包含不安全代码的名为 prog1.cs 的程序,需要在命令行中输入如下命令:
csc /unsafe prog1.cs
在 Visual Studio IDE 环境中编译不安全代码,需要在项目属性中启用相关设置。
步骤如下:
- 双击资源管理器中的属性节点,打开项目属性。
- 点击 Build 标签页。
- 选择选项 "Allow unsafe code"。
C# 多线程
由 小路依依 创建, 最后一次修改 2016-08-12
多线程
线程的定义是程序的执行路径。每个线程都定义了一个独特的控制流,如果应用程序涉及到复杂且耗时的操作,那么设置不同的线程执行路径会非常有好处,因为每个线程会被指定于执行特定的工作。
线程实际上是轻量级进程。一个常见的使用线程的实例是现代操作系统中的并行编程。使用线程不仅有效地减少了 CPU 周期的浪费,同时还提高了应用程序的运行效率。
到目前为止我们所编写的程序都是以一个单线程作为应用程序的运行的,其运行过程均为单一的。但是,在这种情况下,应用程序在同一时间只能执行一个任务。为了使应用程序可以同时执行多个任务,需要将其被划分为多个更小的线程。
线程的声明周期
线程的生命周期开始于对象的 System.Threading.Thread 类创建时,结束于线程被终止或是完成执行时。下列各项为线程在生命周期中的各种状态:
- 未启动状态:线程实例已被创建但 Start 方法仍未被调用时的状态。
- 就绪状态:线程已准备好运行,正在等待 CPU 周期时的状态。
- 不可运行状态:下面的几种情况下线程是不可运行的:
- 已经调用 Sleep 方法
- 已经调用 Wait 方法
- 通过 I/O 操作阻塞
- 死亡状态:线程已完成执行或已终止的状态。
主线程
在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。
当 C# 程序开始执行时,会自动创建主线程。使用 Thread 类创建的线程被主线程的子线程调用。通过 Thread 类的 CurrentThread 属性可以访问线程。
下面的程序演示了主线程的执行:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class MainThreadProgram
{
static void Main(string[] args)
{
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果:
This is MainThread
Thread 类的属性和方法
下表为 Thread 类一些常用的属性:
属性 | 描述 |
---|---|
CurrentContext | 获取线程当前正在执行的线程的上下文。 |
CurrentCulture | 获取或设置当前线程的区域性 |
CurrentPrinciple | 获取或设置线程的当前责任人(针对基于角色的安全性) |
CurrentThread | 获取当前正在运行的线程 |
CurrentUICulture | 获取或设置资源管理器当前所使用的区域性,便于在运行时查找区域性特定的资源 |
ExecutionContext | 获取一个 ExcutionContext 对象,该对象包含有关当前线程的各种上下文信息。 |
IsAlive | 获取一个值,指示当前线程的执行状态。 |
IsBackground | 获取或设置一个值,指示线程是否为后台线程。 |
IsThreadPoolThread | 获取或设置一个值,指示线程是否属于托管线程池。 |
ManagedThreadId | 获取当前托管线程的唯一标识符 |
Name | 获取或设置线程的名称。 |
Priority | 获取或设置一个值,指示线程的调度优先级 |
ThreadState | 获取一个值,指示当前线程的状态。 |
下表为 Thread 类一些常用的方法:
序号 | 方法名和描述 |
---|---|
1 | public void Abort() 在调用此方法的线程上引发 ThreadAbortException,则触发终止此线程的操作。调用此方法通常会终止线程。 |
2 | public static LocalDataStoreSlot AllocateDataSlot() 在所有的线程上分配未命名的数据槽。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
3 | public static LocalDataStoreSlot AllocateNamedDataSlot( string name) 在所有线程上分配已命名的数据槽。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
4 | public static void BeginCriticalRegion() 通知主机将要进入一个代码区域,若该代码区域内的线程终止或发生未经处理的异常,可能会危害应用程序域中的其他任务。 |
5 | public static void BeginThreadAffinity() 通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。 |
6 | public static void EndCriticalRegion() 通知主机将要进入一个代码区域,若该代码区域内的线程终止或发送未经处理的异常,仅会影响当前任务。 |
7 | public static void EndThreadAffinity() 通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。 |
8 | public static void FreeNamedDataSlot(string name) 消除进程中所有线程的名称与槽之间的关联。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
9 | public static Object GetData( LocalDataStoreSlot slot ) 在当前线程的当前域中从当前线程上指定的槽中检索值。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
10 | public static AppDomain GetDomain() 返回当前线程正在其中运行的当前域。 |
11 | public static AppDomain GetDomainID() 返回唯一的应用程序域标识符。 |
12 | public static LocalDataStoreSlot GetNamedDataSlot( string name ) 查找已命名的数据槽。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
13 | public void Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。 |
14 | public void Join() 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。 |
15 | public static void MemoryBarrier() 按如下方式同步内存存取:执行当前线程的处理器在对指令进行重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存存取,再执行 MemoryBarrier 调用之前的内存存取的方式。 |
16 | public static void ResetAbort() 取消当前线程请求的 Abort 操作。 |
17 | public static void SetData( LocalDataStoreSlot slot, Object data ) 在指定槽中,设置当前正在运行中线程的当前域的数据。使用以 ThreadStaticAttribute 属性标记的字段,可获得更好的性能。 |
18 | public void Start() 开始一个线程。 |
19 | public static void Sleep( int millisecondsTimeout ) 令线程暂停一段时间。 |
20 | public static void SpinWait( int iterations ) 令线程等待一段时间,时间长度由 iterations 参数定义。 |
21 | public static byte VolatileRead( ref byte address );public static double VolatileRead( ref double address );public static int VolatileRead( ref int address );public static Object VolatileRead( ref Object address ) 读取字段的值。无论处理器的数目或处理器缓存的状态如何,该值都表示由计算机中任何一个处理器写入的最新值。此方法有不同的重载形式,此处仅给出部分例子。 |
22 | public static void VolatileWrite( ref byte address, byte value );public static void VolatileWrite( ref double address, double value );public static void VolatileWrite( ref int address, int value );public static void VolatileWrite( ref Object address, Object value ) 立即写入一个值到字段中,使该值对计算机中的所有处理器都可见。此方法有不同的重载形式,此处仅给出部分例子。 |
23 | public static bool Yield() 令调用线程执行已准备好在当前处理器上运行的另一个线程,由操作系统选择要执行的线程。 |
线程的创建
线程是通过扩展 Thread 类创建的,扩展而来的 Thread 类调用 Start() 方法即可开始子线程的执行。示例:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
编译执行上述代码段,得到如下结果:
In Main: Creating the Child thread
Child thread starts
线程的管理
Thread 类提供了多种用于线程管理的方法。下面的示例调用了 sleep() 方法来在一段特定时间内暂停线程:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
// 令线程暂停 5000 毫秒
int sleepfor = 5000;
Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000);
Thread.Sleep(sleepfor);
Console.WriteLine("Child thread resumes");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下代码段:
In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes
线程的销毁
使用 Abort() 方法可销毁线程。
在运行时,通过抛出 ThreadAbortException 来终止线程。这个异常无法被捕获,当且仅当具备 finally 块时,才将控制送到 finally 块中。
示例:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
try
{
Console.WriteLine("Child thread starts");
// 执行一些任务,如计十个数
for (int counter = 0; counter <= 10; counter++)
{
Thread.Sleep(500);
Console.WriteLine(counter);
}
Console.WriteLine("Child Thread Completed");
}
catch (ThreadAbortException e)
{
Console.WriteLine("Thread Abort Exception");
}
finally
{
Console.WriteLine("Couldn't catch the Thread Exception");
}
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
// 将主线程停止一段时间
Thread.Sleep(2000);
// 中止子线程
Console.WriteLine("In Main: Aborting the Child thread");
childThread.Abort();
Console.ReadKey();
}
}
}
编译执行上述代码,得到如下结果:
In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception
github:
代码汇总:
#define BeyondDebug
using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
/*
C# 泛型 (OK)
C# 匿名方法 (OK)
C# 委托 (OK)
C# 集合 (OK)
C# 索引器 (OK)
C# 事件 (OK)
C# 属性 (OK)
===============
C# 特性
C# 反射
C# 不安全代码
C# 多线程
*/
namespace cs_11
{
class MainClass
{
// 将整个方法声明为不安全代码(右击项目->生成->常规->允许unsafe代码)
public static unsafe void Main(string[] args)
{
if(0 == 1){
/*
.Net Framework 提供了三种预定义的特性:
1. AttributeUsage
2. Conditional
3. Obsolete*/
BeyondClass.showMessage("in main function");
function1();
}
if (1 == 2)
{
old_method();
}
if (2 == 3)
{
System.Reflection.MemberInfo info = typeof(BoyaClass);
// true means find attribute for its ancestor
object[] attrArr = info.GetCustomAttributes(true);
for (int i = 0; i < attrArr.Length; i++){
Console.WriteLine(attrArr[i]);
}
}
if (3 == 4){
Rectangle rect = new Rectangle(6, 7);
rect.display();
Console.WriteLine();
Type type = typeof(Rectangle);
//遍历 Rectangle 类的 属性 (Custom Attribute on class)
foreach(Object attr in type.GetCustomAttributes(false)){
DebugInfoAttribute debugInfoAttr = (DebugInfoAttribute)attr;
if(null != debugInfoAttr){
Console.WriteLine($"Bug No: {debugInfoAttr.BugNo}");
Console.WriteLine($"Coder: {debugInfoAttr.Coder}");
Console.WriteLine($"Last Reviewed Date: {debugInfoAttr.LastReviewDate}");
Console.WriteLine($"Comment: {debugInfoAttr.Comment}");
}
}
Console.WriteLine();
// 遍历方法属性 (Custom Attribute on method)
foreach(MethodInfo m in type.GetMethods()){
foreach(Attribute attr in m.GetCustomAttributes(true)){
DebugInfoAttribute debugInfoAttr = (DebugInfoAttribute)attr;
if(null != debugInfoAttr){
Console.WriteLine($"Bug No: {debugInfoAttr.BugNo}");
Console.WriteLine($"Coder: {debugInfoAttr.Coder}");
Console.WriteLine($"Last Reviewed Date: {debugInfoAttr.LastReviewDate}");
Console.WriteLine($"Comment: {debugInfoAttr.Comment}");
}
}
}
}
if (4 == 5){
// 当我们需要调用外面DLL中的函数又不能使用DllImport 时,也需要指针来传递这些函数。
int age = 13;
int* p = &age;
// age is 13, address is -1074572616
Console.WriteLine($"age is {age}, address is {(int)p}");
}
if(5 == 6){
int luckyNum = 520;
Console.WriteLine($"before change : {luckyNum}");
changeValueToConst(&luckyNum);
Console.WriteLine($"after change : {luckyNum}");
}
if (6 == 7)
{
// 还可以只将部分代码声明为不安全代码
unsafe{
int luckyNum = 520;
int* p = &luckyNum;
Console.WriteLine($"before change : {luckyNum},address is {(int)p}");
// 使用 ToString() 方法可以检索存储在指针变量所引用位置的数据,解引用
Console.WriteLine($"before change : {p->ToString()}");
*p = 67;
Console.WriteLine($"after change : {luckyNum}");
}
}
if (7 == 8){
int num1 = 6;
int num2 = 7;
int* a = &num1;
int* b = &num2;
Console.WriteLine($"before exchange , num1: {num1}, num2: {num2}");
swap(a, b);
Console.WriteLine($"after exchange , num1: {num1}, num2: {num2}");
}
if(8 == 9){
// 命令行中输入如下命
// csc /unsafe prog1.cs
int[] arr = { 5, 6, 7 };
fixed (int* p = arr)
for (int i = 0; i < 3; i++){
Console.WriteLine("Address of arr[{0}] is {1}", i, (int)(p + i));
Console.WriteLine("Value of arr[{0}] is {1}", i, *(p + i));
}
// 在 Visual Studio IDE 环境中编译不安全代码,需要在项目属性中启用相关设置。
// 1. 双击资源管理器中的属性节点,打开项目属性。
// 2. 点击 Build 标签页。
// 3. 选择选项 "Allow unsafe code"。
}
if(9 == 10){
// 1. 托管代码: 由CLR去执行的代码而不是操作系统去执行的代码,
// 2. 不安全代码: 允许自己使用指针访问内存,但同时又要使用CLR提供的垃圾回收机制、类型安全检查等服务,
// 自定义的指针地址就有可能被CLR垃圾回收机制重新调整位置(所以就引入了fixed)
// fixed 语句设置指向托管变量的指针,并在执行该语句期间"固定"此变量。
// 3. 非托管代码: 绕过CLR,由操作系统直接执行,它有自己的垃圾回收、类型安全检查等服务。
var obj = new Point(6,7);
Console.WriteLine($"origin value is : {obj.x}, {obj.y}");
fixed(int *p1 = &obj.x){
fixed(int *p2 = &obj.y){
swap(p1, p2);
}
}
Console.WriteLine($"after swap, value is : {obj.x}, {obj.y}");
}
if (10 == 11)
{
/*
* 未启动状态:线程实例已被创建但 Start 方法仍未被调用时的状态。
就绪状态:线程已准备好运行,正在等待 CPU 周期时的状态。
不可运行状态:下面的几种情况下线程是不可运行的:
已经调用 Sleep 方法
已经调用 Wait 方法
通过 I/O 操作阻塞
死亡状态:线程已完成执行或已终止的状态。
*/
Thread th = Thread.CurrentThread;
th.Name = "Main_Thread";
Console.WriteLine($"|{"Name",-20} : {th.Name,12}|");
Console.WriteLine($"|{"IsAlive",-20} : {th.IsAlive,12}|");
Console.WriteLine($"|{"IsBackground",-20} : {th.IsBackground,12}|");
Console.WriteLine($"|{"IsThreadPoolThread",-20} : {th.IsThreadPoolThread,12}|");
Console.WriteLine($"|{"ManagedThreadId",-20} : {th.ManagedThreadId,12}|");
Console.WriteLine($"|{"Priority",-20} : {th.Priority,12}|");
Console.WriteLine($"|{"ThreadState",-20} : {th.ThreadState,12}|");
/*
|Name : Main_Thread|
|IsAlive : True|
|IsBackground : False|
|IsThreadPoolThread : False|
|ManagedThreadId : 1|
|Priority : Normal|
|ThreadState : Running|
*/
Thread.Sleep(1000);
Console.WriteLine("hh");
}
if (11 == 12){
ThreadStart runnable = new ThreadStart(methodInSubThread);
Thread th = new Thread(runnable);
th.Start();
// run in sub thread, ThreadId : 4
}
if (12 == 13)
{
ThreadStart runnable = new ThreadStart(subThreadFunction);
Thread th = new Thread(runnable);
th.Start();
}
if(13 == 13){
ThreadStart runnable = new ThreadStart(subThreadMethod);
Thread th = new Thread(runnable);
Console.WriteLine("1. MainThread, will start subThread");
th.Start();
Console.WriteLine("2. MainThread, after start subThread");
Thread.Sleep(2000);
Console.WriteLine("4. MainThread, will abort subThread");
th.Abort();
Console.WriteLine("5. MainThread, after abort subThread");
/*
1. MainThread, will start subThread
2. MainThread, after start subThread
3: sub thread starts
sub thread index : 0
sub thread index : 1
sub thread index : 2
4. MainThread, will abort subThread
5. MainThread, after abort subThread
6. sub thread exception : System.Threading.ThreadAbortException
at (wrapper managed-to-native) System.Threading.Thread.SleepInternal(int)
at System.Threading.Thread.Sleep (System.Int32 millisecondsTimeout) [0x00019] in /Users/builder/jenkins/workspace/build-package-osx-mono/2017-12/external/bockbuild/builds/mono-x64/mcs/class/referencesource/mscorlib/system/threading/thread.cs:741
at cs_11.MainClass.subThreadMethod () [0x00012] in /Users/teeking/gtzb/sg_cs/cs_11/cs_11/Program.cs:275
7. sub thread finally code
*/
}
}
// other static methods ============
static void function1(){
BeyondClass.showMessage("in function 1");
function2();
}
static void function2(){
BeyondClass.showMessage("in function 2");
}
// other static methods ============
// true 编译不通过; false 只是黄色警告
[Obsolete("Do Not Use This Old Method", false)]
static void old_method(){
Console.WriteLine("this is obsolete deprecated method");
}
static void new_method(){
Console.WriteLine("this is new method");
}
// other static methods with pointer============
static unsafe void changeValueToConst(int* dataPointer){
*dataPointer = 67;
}
static unsafe void swap(int *p, int *q){
int temp = *p;
*p = *q;
*q = temp;
}
// other static methods for Thread============
static void methodInSubThread(){
Console.WriteLine($"run in sub thread, ThreadId : {Thread.CurrentThread.ManagedThreadId}");
}
static void subThreadFunction(){
Console.WriteLine($"sub thread start, ThreadId: {Thread.CurrentThread.ManagedThreadId}");
int sleepTime = 5000;
Console.WriteLine($"sub thread ready to sleep {sleepTime / 1000} seconds");
Thread.Sleep(sleepTime);
Console.WriteLine("sub thread resumes");
}
// other static methods for Thread============
static void subThreadMethod(){
try{
Console.WriteLine("3: sub thread starts");
// 执行一些任务,如计十个数
for (int i = 0; i <= 10; i++){
Thread.Sleep(500);
Console.WriteLine($"sub thread index : {i}");
}
}catch(ThreadAbortException e){
Console.WriteLine($"6. sub thread exception : {e.ToString()}");
}finally{
Console.WriteLine("7. sub thread finally code");
}
}
}
// other class ================
class BeyondClass{
[Conditional("BeyondDebug")]
public static void showMessage(string msg){
Console.WriteLine(msg);
}
}
// 自定义特性 class ================
/*
* 创建并使用自定义特性包含四个步骤:
声明自定义特性
构建自定义特性
在目标程序元素上应用自定义特性
通过反射访问特性
*/
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor |
AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property,
AllowMultiple = true)]
class DebugInfoAttribute : System.Attribute{
// field
private int bugNo;
private string coder;
private string lastReviewDate;
public string comment;
// constructor
public DebugInfoAttribute(int bugNo, string coder, string lastReviewDate){
this.bugNo = bugNo;
this.coder = coder;
this.lastReviewDate = lastReviewDate;
}
// property
public int BugNo{
get{
return this.bugNo;
}
}
public string Coder{
get{
return this.coder;
}
}
public string LastReviewDate{
get{
return this.lastReviewDate;
}
}
public string Comment{
get{
return this.comment;
}
set{
this.comment = value;
}
}
}
// use custom attribute for class
[DebugInfoAttribute(1, "Beyond", "2018-08-01", Comment = "return type mismatch")]
[DebugInfoAttribute(2, "Mathilda", "2018-08-02", Comment = "Unused variable")]
class Rectangle{
protected double width;
protected double height;
// constructor
public Rectangle(double w,double h){
this.width = w;
this.height = h;
}
// use custom attribute for method
[DebugInfoAttribute(3, "Menma", "2018-08-03", Comment = "Use obsolete method")]
public double getArea(){
return width * height;
}
// use custom attribute for method
[DebugInfoAttribute(4, "Saber", "2018-08-04", Comment = "excalibur.r.r.r...")]
public void display(){
Console.WriteLine($"width: {this.width}, height: {this.height}, area: {this.getArea()}");
}
}
// 自定义特性 class ================
[AttributeUsage(AttributeTargets.All)]
class CustomAttribute: System.Attribute{
// field
public readonly string url;
private string topic;
// property
public string Topic {
get{
return this.topic;
}
set{
this.topic = value;
}
}
// constructor
public CustomAttribute(string url){
this.url = url;
}
}
[CustomAttribute("infomation on the BoyaClass")]
class BoyaClass{
}
// 自定义 class ================
class Point {
public int x;
public int y;
// constructor
public Point(int x,int y){
this.x = x;
this.y = y;
}
}
}