今天我要说的是C#中的三种委托方式:Func委托,Action委托,Predicate委托以及这三种委托的常见使用场景。
Func,Action,Predicate全面解析
首先来说明Func委托,通过MSDN我们可以了解到,Func委托有如下的5种类型:
(1) *delegate TResult Func<TResult>();
(2)*delegate TResult Func<T1,TResult>(T1 arg1);
(3) *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2);
(4)*delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3);
(5)*delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
其中(1)只能委托无参但是有返回值的函数,TResult就是其返回类型。
而(2)只能委托具有一个传入参数,有返回值的函数,T1为一个传入参数,TResult为返回类型。
(3)只能委托具有二个传入参数,有返回值的函数,T1和T2为两个传入参数,TResult为返回类型,(4)和(5)以此类推。
那么如何来使用呢? 下面给出一个简单的几个例子:
1 #region Func委托 2 3 ///Func<TResult>的用法 4 ///这里TResult代表函数的返回值类型 5 ///只能代理返回值为TResult类型的无参函数 6 Func<string> func = delegate() 7 { 8 return "我是Func<TResult>委托出来的结果"; 9 }; 10 Console.WriteLine(func()); 11 Console.ReadKey(); 12 13 ///Func<T,TResult>的用法 14 ///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型 15 ///只能代理参数为T类型,返回值为TResult类型的函数 16 Func<string, string> funcOne = delegate(string s) 17 { 18 return s.ToUpper(); 19 }; 20 Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果")); 21 Console.ReadKey(); 22 23 ///Func<T1,T2,TResult>的用法 24 ///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型 25 ///只能代理参数为T1,T2类型,返回值为TResult类型的函数 26 Func<string, string, string> funcTwo = delegate(string value1, string value2) 27 { 28 return value1 + " " + value2; 29 }; 30 Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果")); 31 Console.ReadKey(); 32 33 #endregion
上面代码中,我用了匿名方法来代替函数,其中delegate()代表无参函数,delegate(string s)代表有一个传入参数的函数,以下的以此类推。
然后需要说明的就是Action委托,这个委托也是非常常用的,尤其是在涉及到线程和界面交互的时候,配合着lamada表达式使用,非常方便的实现二者的交互。后面我会提到用法。
来看看Action委托的几种表现形式:
(1) * delegate void Action(); 无参,无返回值
(2)* delegate void Action<T>(T1 arg1);
(3)* delegate void Action<T1,T2>(T1 arg1, T2 arg2);
(4)* delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3);
(5)* delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4);
从上面可以看出,总共有5中表现形式,其中(1)既没有传入参数,也没有返回值,那么它适合代理那些无参,无返回值的函数;(2)有一个传入参数,无返回值,适合代理有参,无返回值的函数,(3)(4)(5)以此类推。最都容纳四个传入参数。
那么如何使用呢?下面有一些简单的例子:
1 #region Action的用法 2 ///Action<T>的用法 3 ///这里的T为代理函数的传入类型,无返回值 4 Action<string[]> action = delegate(string[] x) 5 { 6 var result = from p in x 7 where p.Contains("s") 8 select p; 9 foreach (string s in result.ToList()) 10 { 11 Console.WriteLine(s); 12 } 13 }; 14 string[] str={ "charlies","nancy","alex","jimmy","selina"}; 15 action(str); 16 Console.ReadKey(); 17 18 #endregion
上面的例子是通过传入的String类型的数组,找出其中包含有字符s的项,然后输出到控制台。
最后一个就是Predicate委托,这个的形式比较少一些,就是一个传入参数,返回值为bool类型,具体示例如下:
1 #region Predicate 2 ///bool Predicate<T>的用法 3 ///输入一个T类型的参数,返回值为bool类型 4 Predicate<string[]> predicate = delegate(string[] x) 5 { 6 var result = from p in x 7 where p.Contains("s") 8 select p; 9 if (result.ToList().Count > 0) 10 { 11 return true; 12 } 13 else 14 { 15 return false; 16 } 17 }; 18 string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" }; 19 if (predicate(_value)) 20 { 21 Console.WriteLine("They contain."); 22 } 23 else 24 { 25 Console.WriteLine("They don't contain."); 26 } 27 Console.ReadKey(); 28 29 #endregion
上面的代码其实也是判断String数组中有没有包含s的项,有的话就在控制台打印出 They contain.没有的话就打印出They don't contain.
总结一下这三个的特点就是:
Func可以接受0个至4个传入参数,必须具有返回值
Action可以接受0个至4个传入参数,无返回值
Predicate只能接受一个传入参数,返回值为bool类型
下面附上全部实现代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace DelegateIntegrateConsoleApp 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 #region Func委托 13 14 ///Func<TResult>的用法 15 ///这里TResult代表函数的返回值类型 16 ///只能代理返回值为TResult类型的无参函数 17 Func<string> func = delegate() 18 { 19 return "我是Func<TResult>委托出来的结果"; 20 }; 21 Console.WriteLine(func()); 22 Console.ReadKey(); 23 24 ///Func<T,TResult>的用法 25 ///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型 26 ///只能代理参数为T类型,返回值为TResult类型的函数 27 Func<string, string> funcOne = delegate(string s) 28 { 29 return s.ToUpper(); 30 }; 31 Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果")); 32 Console.ReadKey(); 33 34 ///Func<T1,T2,TResult>的用法 35 ///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型 36 ///只能代理参数为T1,T2类型,返回值为TResult类型的函数 37 Func<string, string, string> funcTwo = delegate(string value1, string value2) 38 { 39 return value1 + " " + value2; 40 }; 41 Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果")); 42 Console.ReadKey(); 43 44 /*************余下的类似上面的这种操作,最多可以接受四个传入参数*************** 45 *delegate TResult Func<TResult>(); 46 *delegate TResult Func<T1,TResult>(T1 arg1); 47 *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2); 48 *delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3); 49 *delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4); 50 */ 51 52 #endregion 53 54 #region Action的用法 55 ///Action<T>的用法 56 ///这里的T为代理函数的传入类型,无返回值 57 Action<string[]> action = delegate(string[] x) 58 { 59 var result = from p in x 60 where p.Contains("s") 61 select p; 62 foreach (string s in result.ToList()) 63 { 64 Console.WriteLine(s); 65 } 66 }; 67 string[] str={ "charlies","nancy","alex","jimmy","selina"}; 68 action(str); 69 Console.ReadKey(); 70 71 /***************余下的类似上面的这种操作,最多可以接受四个传入参数********** 72 * delegate void Action(); 无参,无返回值 73 * delegate void Action<T>(T1 arg1); 74 * delegate void Action<T1,T2>(T1 arg1, T2 arg2); 75 * delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3); 76 * delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4); 77 */ 78 79 #endregion 80 81 #region Predicate 82 ///bool Predicate<T>的用法 83 ///输入一个T类型的参数,返回值为bool类型 84 Predicate<string[]> predicate = delegate(string[] x) 85 { 86 var result = from p in x 87 where p.Contains("s") 88 select p; 89 if (result.ToList().Count > 0) 90 { 91 return true; 92 } 93 else 94 { 95 return false; 96 } 97 }; 98 string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" }; 99 if (predicate(_value)) 100 { 101 Console.WriteLine("They contain."); 102 } 103 else 104 { 105 Console.WriteLine("They don't contain."); 106 } 107 Console.ReadKey(); 108 109 #endregion 110 111 112 } 113 } 114 }
m和用Func,Action,Predicate进行线程UI交互
下面这部分主要讲解如何在WinForm中利用这些委托进行线程和界面的交互。
首先对于Func来说,由于其必须具有返回值,所以我们可以利用如下代码来实现线程和界面的交互:
1 #region 利用Func实现线程和界面交互 2 private void AlternationUsingFunc(object text) 3 { 4 //无参数,但是返回值为bool类型 5 this.Invoke(new Func<bool>(delegate() 6 { 7 button1.Text = text.ToString(); 8 return true; //返回值 9 })); 10 } 11 12 private void AlternationUsingFuncThread() 13 { 14 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc); 15 ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用"); 16 } 17 18 private void button1_Click(object sender, EventArgs e) 19 { 20 AlternationUsingFuncThread(); 21 } 22 #endregion
其中
1 this.Invoke(new Func<bool>(delegate() 2 { 3 button1.Text = text.ToString(); 4 return true; //返回值 5 }));
这段代码中利用了Func<TResult>这种类型,也就是没有传入参数,但是有一个bool类型的返回值,然后将这个函数利用加入到线程池中,最后运行,这里我们成功的设置了button1的text为“Func的使用”。
然后,对于Action来说,由于其可以无参,无返回值,那么它的交互方式最为简便,同时也是使用最多的,先看有参的调用方式:
1 #region 利用Action实现线程和界面交互 2 private void AlternationUsingAction(object text) 3 { 4 //需要一个T类型的参数,无返回值 5 this.Invoke(new Action<object>(delegate(object myText) 6 { 7 myText = text; 8 button2.Text = text.ToString(); 9 }),text); 10 } 11 12 private void AlternationUsingActionThread() 13 { 14 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction); 15 ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用"); 16 } 17 18 private void button2_Click(object sender, EventArgs e) 19 { 20 AlternationUsingActionThread(); 21 } 22 #endregion
在上面的代码示例中,我们使用了带有一个传入参数的Action委托,当然了,匿名类型delegate(object myText)匿名代理了具有一个传入参数的函数。
其实简单点来说,可以像如下方式使用:
1 this.Invoke((Action)(()=> 2 { 3 button2.Text = text.ToString(); 4 }));
这样就显得非常的方便。
最后一个当然是Predicate委托,和上面类似,只是写起来麻烦一些,它需要一个传入参数,并且返回一个bool类型:
1 #region 利用Predicate实现线程和界面的交互 2 private void AlternationUsingPrecidate(object text) 3 { 4 //需要一个T类型的参数,返回bool类型 5 this.Invoke(new Predicate<object>(delegate(object myText) 6 { 7 myText = text; 8 button3.Text = myText.ToString(); 9 return true; //返回值 10 }),text); 11 } 12 13 private void AlternationUsingPrecidateThread() 14 { 15 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate); 16 ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用"); 17 } 18 19 private void button3_Click(object sender, EventArgs e) 20 { 21 AlternationUsingPrecidateThread(); 22 } 23 #endregion
具体的注释我已经写在代码中了,最后运行,能成功的将button3的Text置为“Predicate的使用.”
下面是全部实现代码:
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Threading; 10 11 namespace DelegateIntegrateWinFormApp 12 { 13 public partial class mainFrm : Form 14 { 15 public mainFrm() 16 { 17 InitializeComponent(); 18 } 19 20 private void mainFrm_Load(object sender, EventArgs e) 21 { 22 /****************************注意例子中的使用方法**************** 23 * delegate TResult Func<TResult>(); 无参,但是返回值为TResult类型 24 * delegate void Action<T>(T1 arg1); 有一个参数arg1,但是无返回值 25 * delegate bool Predicate<T>(T arg); 有一个参数arg,返回bool类型 26 * **************************************************************/ 27 } 28 29 #region 利用Func实现线程和界面交互 30 private void AlternationUsingFunc(object text) 31 { 32 //无参数,但是返回值为bool类型 33 this.Invoke(new Func<bool>(delegate() 34 { 35 button1.Text = text.ToString(); 36 return true; //返回值 37 })); 38 } 39 40 private void AlternationUsingFuncThread() 41 { 42 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc); 43 ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用"); 44 } 45 46 private void button1_Click(object sender, EventArgs e) 47 { 48 AlternationUsingFuncThread(); 49 } 50 #endregion 51 52 53 54 55 #region 利用Action实现线程和界面交互 56 private void AlternationUsingAction(object text) 57 { 58 //需要一个T类型的参数,无返回值 59 this.Invoke(new Action<object>(delegate(object myText) 60 { 61 myText = text; 62 button2.Text = text.ToString(); 63 }),text); 64 } 65 66 private void AlternationUsingActionThread() 67 { 68 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction); 69 ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用"); 70 } 71 72 private void button2_Click(object sender, EventArgs e) 73 { 74 AlternationUsingActionThread(); 75 } 76 #endregion 77 78 #region 利用Predicate实现线程和界面的交互 79 private void AlternationUsingPrecidate(object text) 80 { 81 //需要一个T类型的参数,返回bool类型 82 this.Invoke(new Predicate<object>(delegate(object myText) 83 { 84 myText = text; 85 button3.Text = myText.ToString(); 86 return true; //返回值 87 }),text); 88 } 89 90 private void AlternationUsingPrecidateThread() 91 { 92 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate); 93 ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用"); 94 } 95 96 private void button3_Click(object sender, EventArgs e) 97 { 98 AlternationUsingPrecidateThread(); 99 } 100 #endregion 101 102 103 104 105 } 106 }
那么,现在对于WPF来说,该如何来使用呢?其实在WPF中,和winform中类似,只是在WPF中要实现线程和界面的交互,我们需要用Dispatcher来实现,也就是形如Control.Dispatcher.Invoke()的方式,由于与Winform实现方式无多大差别,这里我就直接附上全部代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows; 6 using System.Windows.Controls; 7 using System.Windows.Data; 8 using System.Windows.Documents; 9 using System.Windows.Input; 10 using System.Windows.Media; 11 using System.Windows.Media.Imaging; 12 using System.Windows.Navigation; 13 using System.Windows.Shapes; 14 using System.Threading; 15 16 namespace WpfApplication1 17 { 18 /// <summary> 19 /// Interaction logic for Window1.xaml 20 /// </summary> 21 public partial class Window1 : Window 22 { 23 public Window1() 24 { 25 InitializeComponent(); 26 } 27 28 private void Window_Loaded(object sender, RoutedEventArgs e) 29 { 30 /****************************注意例子中的使用方法**************** 31 * delegate TResult Func<TResult>(); 无参,但是返回值为TResult类型 32 * delegate void Action(); 无参,无返回值 33 * delegate bool Predicate<T>(T arg); 有一个参数arg,返回bool类型 34 * 需要注意,与WinForm中不同的是,WPF中需要利用Control.Dispatcher.Invoke来实现,其他类似. 35 * **************************************************************/ 36 } 37 38 #region 利用Func实现线程和界面交互 39 private void AlternationUsingFunc(object text) 40 { 41 //无参数,但是返回值为bool类型 42 button1.Dispatcher.Invoke(new Func<bool>(delegate() 43 { 44 button1.Content = text.ToString(); 45 return true; //返回值 46 })); 47 } 48 49 private void AlternationUsingFuncThread() 50 { 51 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingFunc); 52 ThreadPool.QueueUserWorkItem(waitCallBack, "Func的使用"); 53 } 54 55 private void button1_Click(object sender, RoutedEventArgs e) 56 { 57 AlternationUsingFuncThread(); 58 } 59 #endregion 60 61 #region 利用Action实现线程和界面交互 62 private void AlternationUsingAction(object text) 63 { 64 //无参数,无返回值 65 //button2.Dispatcher.Invoke(new Action(delegate() 66 //{ 67 // button2.Content = text.ToString(); 68 //})); 69 //或者 70 button2.Dispatcher.Invoke((Action)(()=> 71 { 72 button2.Content = text.ToString(); 73 })); 74 } 75 76 private void AlternationUsingActionThread() 77 { 78 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction); 79 ThreadPool.QueueUserWorkItem(waitCallBack, "Action的使用"); 80 } 81 82 private void button2_Click(object sender, RoutedEventArgs e) 83 { 84 AlternationUsingActionThread(); 85 } 86 #endregion 87 88 #region 利用Predicate实现线程和界面的交互 89 private void AlternationUsingPrecidate(object text) 90 { 91 //需要一个T类型的参数,返回bool类型 92 this.button3.Dispatcher.Invoke(new Predicate<object>(delegate(object myText) 93 { 94 myText = text; 95 button3.Content = myText.ToString(); 96 return true; //返回值 97 }), text); 98 } 99 100 private void AlternationUsingPrecidateThread() 101 { 102 WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate); 103 ThreadPool.QueueUserWorkItem(waitCallBack, "Predicate的使用"); 104 } 105 106 private void button3_Click(object sender, RoutedEventArgs e) 107 { 108 AlternationUsingPrecidateThread(); 109 } 110 #endregion 111 112 113 } 114 }
逐个点击界面上的按钮,我们可以看到成功实现了线程和UI的交互:
当然,上面我们只是说到了在WinForm中和WPF中如何来使用的情况,代码比较简单,也没有具体的应用场景,下面我们将结合中WPF来模拟一个具体的应用场景:
实现异步和线程同步
现在假设我有一个txt文档,名称为newEXO.txt,里面大概有5w行记录,文件大小为30MB左右;同时我手边还有一个oldEXO.txt里面也有5w数据,但是其中有一些记录和newEXO.txt中的不同,我现在需要对比两个txt文档,找出不同的记录,并对不同的记录进行上色操作。
那么现在这里很明确了,我们需要两个函数,一个是读取记录的函数ChangeText(),一个是上色的函数ChangeColor()。
但是在实际操作中,发现如果我直接利用wpf读取数据的话,逐行读取,耗时10s左右,也就是用户界面会被阻塞10s,然后才会显示给用户,这个体验性是相当不好的,所以拟采用异步方式来导入数据。
同时,考虑到差异比较也会比较耗时,所以也准备采用异步方式来进行对比。
那么问题来了,两个均采用异步方式进行,难免会发生数据未导入完成就开始进行差异比较的可能,所以这里还涉及到一个线程同步的问题。
现在,这里有三个操作,异步的数据导入,异步的差异比较并上色,线程同步。
首先我们看异步导入,你也可以自己实现一个异步类,通过委托的BeginInvoke方法和EndInvoke方法来实现
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ThreadSynchorous 7 { 8 public class AsyncInvoke 9 { 10 public void BeginAsync(Func<bool> MyFunction) 11 { 12 Func<bool> func = new Func<bool>(MyFunction); 13 IAsyncResult iar = func.BeginInvoke(new AsyncCallback(EndAsync), func); 14 } 15 16 public void EndAsync(IAsyncResult iar) 17 { 18 Func<bool> func = (Func<bool>)iar.AsyncState; 19 func.EndInvoke(iar); 20 } 21 } 22 }
由于Action委托的使用方式最为便捷,这里我采用Action委托方式来进行,当然了,:
1 private void ChangeText() 2 { 3 this.button1.Dispatcher.Invoke((Action)(()=> 4 { 5 string filename = @"C: ewEXO.txt"; 6 using (StreamReader sr = new StreamReader(filename, Encoding.Default)) 7 { 8 string result; 9 while ((result = sr.ReadLine()) != null) 10 { 11 //here perform action 12 } 13 } 14 //label1.Dispatcher.Invoke((new Action(delegate() 15 label1.Dispatcher.Invoke((Action)(()=> 16 { 17 label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") "; 18 })); 19 })); 20 }
首先是当点击button1按钮的时候,就启动ChangeText()函数,也即数据导入函数,然后label1会在加载完毕的时候,给出提示信息。
下面再看看ChangeColor()函数:
1 private void ChangeColor() 2 { 3 this.button1.Dispatcher.Invoke((Action)(()=> 4 { 5 this.button1.Background = Brushes.Red; 6 7 //here perform large amount of data action and color the result 8 9 label1.Dispatcher.Invoke((Action)(()=> 10 { 11 label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") "; 12 })); 13 14 })); 15 }
可以看到也是当button1点击的时候,会触发ChangeColor函数。由于二者操作比较耗时,为了防止用户界面阻塞,我们放到线程池中:
ThreadPool.QueueUserWorkItem(o => ChangeText());
ThreadPool.QueueUserWorkItem(o => ChangeColor());
从上面可以看出,当点击按钮button1的时候,两个函数同时被引发,也就是点击按钮的时候进行如下操作:
1 private void button1_Click(object sender, RoutedEventArgs e) 2 { 3 ThreadPool.QueueUserWorkItem(o => ChangeText()); 4 ThreadPool.QueueUserWorkItem(o => ChangeColor()); 5 label1.Content += " ------------------------- "; 6 }
看到了什么?
看到了线程运行的混乱,我们本想让数据先加载,然后比较得出差异着色,可惜上面的结果中却与想象中的相差甚远.
这里的ChangeText()函数和ChangeColor()函数肯定不会像想象的那样顺序执行,那么代码就有问题了,所以为了避免这个问题,我们必须进行线程同步,如何来进行呢? 方法很多,这里我采用EventWaitHandle方式来进行。
EventWaitHandle的Reset方式用来重置信号量,告诉其他运行的进程,你们需要被阻塞;Set方式用来释放信号量,告诉其他运行的进程,你们的阻塞已经被解除,可以继续运行了。
但是其他进行通过什么来知道自己是否可以解除阻塞状态呢? 那就是利用WaitOne方式来判断:
也就是按照如下的代码模式来:
1 EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset); 2 private void ChangeText() 3 { 4 waitMeHandle.Reset(); //即将进入下列执行过程,其他需要阻塞 5 //.... 6 waitMeHandle.Set(); //释放 7 } 8 private void ChangeColor() 9 { 10 waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行 11 // ... 12 }
当然上面我举出的例子只是一个Sample,我写过这个软件,利用的是BeginInvoke和EndInvoke方式实现的异步调用,有兴趣可以参见我的这篇文章中提到的软件:
我的作品:PlainTextCompare
下面是软件截图:
另外在写这篇文章的时候,我在StackOverFlow上面有过提问,就是关于当前的Thread的ThreadId为什么一致的问题, 应该说两个函数放到了ThreadPool中,结果出来的ThreadId应该不一样才对呀.
其实,正确的答案是我的打印出ThreadId的信息都放在了label1.Dispatcher.Invoke这句话中,而这个Lable1属于界面UI,也就是前台线程,所以ThreadId会是一样的,如果你直接在进入函数的时候,输出ThreadId,你就会发现两个函数运行在不同的线程上了.
下面是全部代码:
1 using System; 2 using System.Text; 3 using System.Windows; 4 using System.Windows.Media; 5 using System.Threading; 6 using System.IO; 7 8 namespace ThreadSynchorous 9 { 10 public partial class Window1 : Window 11 { 12 public Window1() 13 { 14 InitializeComponent(); 15 asyncInvoke = new AsyncInvoke(); 16 } 17 AsyncInvoke asyncInvoke; 18 EventWaitHandle waitMeHandle = new EventWaitHandle(false,EventResetMode.ManualReset); 19 20 private void button1_Click(object sender, RoutedEventArgs e) 21 { 22 ThreadPool.QueueUserWorkItem(o => ChangeText()); 23 ThreadPool.QueueUserWorkItem(o => ChangeColor()); 24 25 label1.Content += " ------------------------- "; 26 } 27 28 29 private void ChangeText() 30 { 31 waitMeHandle.Reset(); //即将进入下列执行过程,其他需要阻塞 32 this.button1.Dispatcher.Invoke((Action)(()=> 33 { 34 string filename = @"C:MyLearneqrms_hk_20111219_listedposn_ffEQRMS_HK_20111219_EXO.txt"; 35 using (StreamReader sr = new StreamReader(filename, Encoding.Default)) 36 { 37 string result; 38 while ((result = sr.ReadLine()) != null) 39 { 40 //here perform action 41 } 42 } 43 label1.Dispatcher.Invoke((Action)(()=> 44 { 45 label1.Content += "Loading finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId.ToString() + ") "; 46 waitMeHandle.Set(); //释放 47 })); 48 })); 49 } 50 51 52 private void ChangeColor() 53 { 54 waitMeHandle.WaitOne(); //等待,直到接收到Set信号,才能运行 55 this.button1.Dispatcher.Invoke((Action)(()=> 56 { 57 this.button1.Background = Brushes.Red; 58 59 //here perform large amount of data action and color the result 60 61 label1.Dispatcher.Invoke((Action)(()=> 62 { 63 label1.Content += "Coloring finish!(Thread.CurrentThreadName=" + Thread.CurrentThread.ManagedThreadId + ") "; 64 })); 65 66 })); 67 } 68 } 69 }
本文中涉及到的源码,可以从这里下载
如果你感觉这篇文章对你有帮助,帮我点一下[推荐]哦,谢谢!
转自:http://www.cnblogs.com/scy251147/archive/2012/01/05/2313805.html