前言
学习C#两个多月了,像当初实习做PHP开发一样,也是由着一个个Feature需求,慢慢掌握了很多相关的编程技巧。本次主要记录下学习C# 多线程的相关知识。
参考书籍:《Csharp高级编程(第7版)》
1.使用线程的原因
不过运行多个线程也要注意一些问题:他们可以同时运行,但是如果线程访问相同的数据,就很容易出问题,必须实现同步机制。
2.理解线程
线程是程序中独立的指令流。C#编写的程序都有一个入口点(即Main()方法),程序从该方法的第一条开始执行,直到方法返回为止。这种程序结构非常适合用于一个有任务序列的程序,但是程序常常需要同时完成多个任务。
这就要用到多个线程,比如Word的拼写检查器的工作原理是这样的:一个线程等待用户输入,另一个线程进行后台搜索,第3个线程将写入的数据保存在临时文件中,第4个线程从Internet上下载其他数据。
理解线程很重要的一点其实是理解线程和进程的关系:
3.创建线程的方式
- 异步委托
创建线程的一种简单地方式是定义委托,并异步调用它。委托是方法的类型安全的引用,它使用线程池来完成异步任务。
代码实例:
使用投票的例子,并检查委托是否完成了它的任务。等待异步委托结果的四种方式:
(1)轮询
Delegate类提供了BeginInvoke()方法,通过其返回类型IAsyncResult ,可以获取委托的相关信息,并检验它是否完成了任务。只要委托没有完成其任务,程序的主线程就继续执行while循环。
(2)等待句柄
使用与IAsyncResult 相关联的等待句柄。使用AsyncWaitHandle属性可以访问等待句柄,该属性可以返回一个WaitHandle类型的对象,它可以等待委托线程完成其任务。
(3)异步回调
(4)Lambda表达式
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Diagnostics; namespace Wrox.ProCSharp.Threading { class Program { static int TakesAWhile(int data, int ms) { Console.WriteLine("TakesAWhile started"); Thread.Sleep(ms); Console.WriteLine("TakesAWhile completed"); return ++data; } //要从委托中调用这个方法,必须定义一个有相同参数和返回类型的的委托 public delegate int TakesAWhileDelegate(int data, int ms); static void Main() { // synchronous // TakesAWhile(1, 3000); TakesAWhileDelegate d1 = TakesAWhile; // (1)polling轮询 //IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null); //while (!ar.IsCompleted) //{ // // doing something else // Console.Write("."); // Thread.Sleep(50); //} //int result = d1.EndInvoke(ar); //Console.WriteLine("result: {0}", result); // (2)wait handle //IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null); //while (true) //{ // Console.Write("."); // if (ar.AsyncWaitHandle.WaitOne(50, false)) // { // Console.WriteLine("Can get the result now"); // break; // } //} //int result = d1.EndInvoke(ar); //Console.WriteLine("result: {0}", result); // (3)async callback //d1.BeginInvoke(1, 3000, TakesAWhileCompleted, d1); //for (int i = 0; i < 100; i++) //{ // Console.Write("."); // Thread.Sleep(50); //} //(4)Lambda expression:可以直接访问作用域外的变量d1,所以不需要把一个值赋予BeginInvoke()方法的最后一个参数 d1.BeginInvoke(1, 3000, ar => { int result = d1.EndInvoke(ar); Console.WriteLine("result: {0}", result); }, null); for (int i = 0; i < 100; i++) { Console.Write("."); Thread.Sleep(50); } } static void TakesAWhileCompleted(IAsyncResult ar) { if (ar == null) throw new ArgumentNullException("ar"); TakesAWhileDelegate d1 = ar.AsyncState as TakesAWhileDelegate; Trace.Assert(d1 != null, "Invalid object type"); int result = d1.EndInvoke(ar); Console.WriteLine("result: {0}", result); } } }
运行结果:
ps:在运行书中附带的sample时,报错:
经查证,发现这是由于StartupUri中的内容与窗口名称不一致所导致。
这部分的知识可以参考一篇译文:WPF教程(十)使用App.xaml