线程、任务和同步学习笔记(三)
1、要使用Task类,必须要引用System.Threading.Tasks命名空间。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 // Using task factory 10 TaskFactory factory = new TaskFactory(); 11 Task task1 = factory.StartNew(TaskMethod); 12 13 Thread.Sleep(10); 14 } 15 16 static void TaskMethod() 17 { 18 Console.WriteLine("Running in a task."); 19 Console.WriteLine("Task id: {0}.", Task.CurrentId); 20 } 21 }
运行结果:
若没有第11行代码,则一定不会输出结果。但如果Sleep方法的参数很小(如设置为1),则出现上图中所示结果的概率也会很小。
2、创建任务的方式有3种,分别是使用TaskFactory对象的StartNew方法、Task类的静态Factory属性的StartNew方法以及Task类的构造函数。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 // Using task factory 10 TaskFactory factory = new TaskFactory(); 11 Task task1 = factory.StartNew(TaskMethod); 12 13 // Using the task factory via a task 14 Task task2 = Task.Factory.StartNew(TaskMethod); 15 16 // Using Task constructor 17 Task task3 = new Task(TaskMethod); 18 task3.Start(); 19 20 Thread.Sleep(10); 21 } 22 23 static void TaskMethod() 24 { 25 Console.WriteLine("Running in a task."); 26 Console.WriteLine("Task id: {0}.", Task.CurrentId); 27 } 28 }
运行结果:
3、以上3种方式都可以传递TaskCreationOptions枚举中的值。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 // Using task factory 10 TaskFactory factory = new TaskFactory(); 11 Task task1 = factory.StartNew(TaskMethod); 12 13 // Using the task factory via a task 14 Task task2 = Task.Factory.StartNew(TaskMethod); 15 16 // Using Task constructor 17 Task task3 = new Task(TaskMethod); 18 task3.Start(); 19 20 Task task4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness); 21 task4.Start(); 22 Thread.Sleep(10); 23 } 24 25 static void TaskMethod() 26 { 27 Console.WriteLine("Running in a task."); 28 Console.WriteLine("Task id: {0}.", Task.CurrentId); 29 } 30 }
运行结果:
4、相较于线程,任务具有连续性,可以指定在任务完成后,应开始执行的另一个特定任务。例如,一个使用前一个任务的结果的新任务,即如果前一个任务失败了,这个任务就应执行一些清理工作。使用Task类对象的ContinueWith方法可以实现连续任务的连接。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Task task1 = new Task(DoOnFirst); 10 task1.Start(); 11 Task task2 = task1.ContinueWith(DoOnSecond); 12 Task task3 = task1.ContinueWith(DoOnSecond); 13 Task task4 = task3.ContinueWith(DoOnSecond); 14 Thread.Sleep(1000); 15 } 16 17 static void DoOnFirst() 18 { 19 Console.WriteLine("Do some task, the task id: {0}.", Task.CurrentId); 20 Thread.Sleep(100); 21 } 22 23 static void DoOnSecond(Task task) 24 { 25 Console.WriteLine("Task {0} finished.", task.Id); 26 Console.WriteLine("This task id: {0}.", Task.CurrentId); 27 Console.WriteLine("Do some cleanup..."); 28 Thread.Sleep(100); 29 } 30 }
运行结果:
从上图运行结果分析,ContinueWith方法会将其参数所指定的内容创建为新的任务并运行。然而,执行的次序并不能确定。
5、利用任务的连续性,可以在一个任务结束后启动另一个任务,从而构成一个层次结构(或者称其为父子结构)。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Task parent = new Task(ParentTask); 10 parent.Start(); 11 Thread.Sleep(2000); 12 Console.WriteLine("Status: " + parent.Status); 13 Thread.Sleep(4000); 14 Console.WriteLine("Status: " + parent.Status); 15 } 16 17 static void ParentTask() 18 { 19 Console.WriteLine("Task id {0}.", Task.CurrentId); 20 Task child = new Task(ChildTask); 21 child.Start(); 22 Thread.Sleep(1000); 23 Console.WriteLine("Parent finished, and start child."); 24 } 25 26 static void ChildTask() 27 { 28 Console.WriteLine("Child started."); 29 Thread.Sleep(5000); 30 Console.WriteLine("Child finished."); 31 } 32 }
运行结果:
6、由上述第4条可知,任务是可以有结果的。任务结束时,可以将某些有用的信息写到线程安全的共享对象中。或者使用Task类的泛型版本,定义返回某个结果的任务的返回类型。然而,在C# 5以后,Task类对象的Result属性已经不存在了,即《C# 高级编程(中文第七版)》中如下的例子不能正常编译。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Task task = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(8, 3)); 10 task.Start(); 11 Console.WriteLine(task.Result); 12 } 13 14 static Tuple<int, int> TaskWithResult(object obj) 15 { 16 Tuple<int, int> tuple = (Tuple<int, int>)obj; 17 int division = tuple.Item1 / tuple.Item2; 18 int reminder = tuple.Item1 % tuple.Item2; 19 return Tuple.Create<int, int>(division, reminder); 20 } 21 }
编译结果:
不知微软取消该属性的原因为何?且在新版本中该功能如何实现?有待于进一步学习研究。