除了使用上面说到的数据槽之外,我们还有另一种方式,即ThreadStaticAttribute特性。申明了该特性的变量,会被.NET作为线程独享的数据来使用。我们可以将其理解为一种被.NET封装了的TLS机制,本质上,它仍然使用了线程环境块来存放数据。
下面的示例代码展示了ThreadStaticAttribute特性的使用:
class Program { static void Main(string[] args) { Console.WriteLine(); // 创建五个线程来同时运行,但是这里不适合用线程池, (int i = 0; i < 5; i++) { Thread thread = new Thread(ThreadStatic.Work); thread.Start(); } Console.ReadKey(); } } 包含线程静态数据 ThreadStatic { // 值类型的线程静态数据 [ThreadStatic] threadId = 0; Ref refThreadId = new Ref(); 线程执行的方法,操作线程静态数据 Work() { // 存储线程ID,一个应用程序域内线程ID不会重复 threadId = Thread.CurrentThread.ManagedThreadId; refThreadId.Id = Thread.CurrentThread.ManagedThreadId; // 查看一下刚刚插入的数据 Console.WriteLine(, Thread.CurrentThread.ManagedThreadId.ToString(), threadId, refThreadId.Id.ToString()); // 睡眠1s Thread.Sleep(1000); // 查看其他线程的运行是否干扰了当前线程静态数据 Console.WriteLine(, Thread.CurrentThread.ManagedThreadId.ToString(), threadId, refThreadId.Id.ToString()); } } 简单引用类型 Ref { private int id; public int Id { get { return id; } set { id = value; } } }
View Code该实例的执行结果如下图所示,正如我们所看到的,对于使用了ThreadStatic特性的字段,.NET会将其作为线程独享的数据来处理,当某个线程对一个使用了ThreadStatic特性的字段进行赋值后,这个值只有这个线程自己可以看到并访问修改,该值对于其他线程时不可见的。相反,没有标记该特性的,则会被多个线程所共享。
异步模式是在处理流类型时经常采用的一种方式,其应用的领域相当广阔,包括读写文件、网络传输、读写数据库,甚至可以采用异步模式来做任何计算工作。相对于手动编写线程代码,异步模式是一个高效的编程模式。
(1)所谓异步模式是个什么鬼?
所谓的异步模式,是指在启动一个操作之后可以继续执行其他工作而不会发生阻塞。以读取文件为例,在同步模式下,当程序执行到Read方法时,需要等到读取动作结束后才能继续往下执行。而异步模式则可以简单地通知开始读取任务之后,继续其他的操作。 异步模式的优点就在于不需要使当前线程等待,而可以充分地利用CPU时间。
PS:异步模式区别于线程池机制的地方在于其允许程序查看操作的执行状态,而如果利用线程池的后台线程,则无法确切地知道操作的进行状态以及其是否已经结束。
使用异步模式可以通过一些异步聚集技巧来查看异步操作的结果,所谓的聚集技巧是指查看操作是否结束的方法,常用的方式是:在调用BeingXXX方法时传入操作结束后需要执行的方法(又称为回调方法),同时把执行异步操作的对象传入以便执行EndXXX方法。
(2)使用异步模式读取一个文件
下面的示例代码中:
① 主线程中负责开始异步读取并传入聚集时需要使用的方法和状态对象:
partial class Program { testFile = ; bufferSize = 1024; static void Main(string[] args) { (File.Exists(testFile)) { File.Delete(testFile); } (FileStream stream = File.Create(testFile)) { ; byte[] contentByte = Encoding.UTF8.GetBytes(content); stream.Write(contentByte, 0, contentByte.Length); } (FileStream stream = new FileStream(testFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.Asynchronous)) { byte[] data = new byte[bufferSize]; // 将自定义类型对象实例作为参数 ReadFileClass rfc = new ReadFileClass(stream, data); // 开始异步读取 IAsyncResult result = stream.BeginRead(data, 0, data.Length, FinshCallBack, rfc); // 模拟做了一些其他的操作 Thread.Sleep(3 * 1000); Console.WriteLine(); } Console.ReadKey(); } }
View Code② 定义了完成异步操作读取之后需要调用的方法,其逻辑是简单地打印出文件的内容: