C#中常见的委托(Func委托、Action委托、Predicate委托)

今天我要说的是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 }
View Code

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:\newEXO.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 += " \r\n-------------------------\r\n";
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,你就会发现两个函数运行在不同的线程上了.

StackOverFlow的问答,请点击这里

下面是全部代码:

 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 += " \r\n-------------------------\r\n";
26         }
27 
28         
29         private void ChangeText()
30         {
31             waitMeHandle.Reset();  //即将进入下列执行过程,其他需要阻塞
32             this.button1.Dispatcher.Invoke((Action)(()=>
33             {
34                 string filename = @"C:\MyLearn\eqrms_hk_20111219_listedposn_ff\EQRMS_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

posted @ 2014-08-05 11:12  银河系上的地球  阅读(1092)  评论(0编辑  收藏  举报