• 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:
    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,你就会发现两个函数运行在不同的线程上了.

    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 += " 
    -------------------------
    ";
    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

  • 相关阅读:
    [转]c#的DateTime.Now函数详解
    PHP学习笔记
    【错误】MsDepSvc.exe 站用了80端口/IIS的0×8ffe2740错误解决方
    IIS连接数
    Mybatis3.2.1整合Spring3.1
    linux常用命令大全
    Spring3.2新注解@ControllerAdvice
    SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解
    Console命令详解,让调试js代码变得更简单
    String.format
  • 原文地址:https://www.cnblogs.com/zxbzl/p/3891813.html
Copyright © 2020-2023  润新知