一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的。

关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微,尤其是张子阳的那一篇。我就不用多废话了。

今天我要说的是C#中的三种委托方式:Func委托,Action委托,Predicate委托以及这三种委托的常见使用场景。

Func,Action,Predicate全面解析

首先来说明Func委托,通过MSDN我们可以了解到,Func委托有如下的5种类型:

  1. 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. 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 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的项,有的话就在控制台打印出  They contain.没有的话就打印出They don't contain.

总结一下这三个的特点就是:

  1. Func可以接受0个至4个传入参数,必须具有返回值
    Action可以接受0个至4个传入参数,无返回值
    Predicate只能接受一个传入参数,返回值为bool类型

下面附上全部实现代码:

  1. using System; using System.Collections.Generic; using System.Linq; using System.Text;
    namespace DelegateIntegrateConsoleApp { class Program { static void Main(string[] args) { #region Func委托 ///Func<TResult>的用法 ///这里TResult代表函数的返回值类型 ///只能代理返回值为TResult类型的无参函数 Func<string> func = delegate() { return "我是Func<TResult>委托出来的结果"; }; Console.WriteLine(func()); Console.ReadKey();
    ///Func<T,TResult>的用法 ///这里的T为代理的函数的传入类型,TResult代表函数的返回值类型 ///只能代理参数为T类型,返回值为TResult类型的函数 Func<string, string> funcOne = delegate(string s) { return s.ToUpper(); }; Console.WriteLine(funcOne("我是Func<T,TResult>委托出来的结果")); Console.ReadKey();
    ///Func<T1,T2,TResult>的用法 ///这里T1,T2为代理的函数的传入类型,TResult代表函数的返回值类型 ///只能代理参数为T1,T2类型,返回值为TResult类型的函数 Func<string, string, string> funcTwo = delegate(string value1, string value2) { return value1 + " " + value2; }; Console.WriteLine(funcTwo("我是", "Func<T1,T2,TResult>委托出来的结果")); Console.ReadKey();
    /*************余下的类似上面的这种操作,最多可以接受四个传入参数*************** *delegate TResult Func<TResult>(); *delegate TResult Func<T1,TResult>(T1 arg1); *delegate TResult Func<T1,T2,TResult>(T1 arg1, T2 arg2); *delegate TResult Func<T1,T2,T3,TResult>(T1 arg1, T2 arg2, T3 arg3); *delegate TResult Func<T1,T2,T3,T4,TResult>T1 arg1, T2 arg2, T3 arg3, T4 arg4); */
    #endregion
    #region Action的用法 ///Action<T>的用法 ///这里的T为代理函数的传入类型,无返回值 Action<string[]> action = delegate(string[] x) { var result = from p in x where p.Contains("s") select p; foreach (string s in result.ToList()) { Console.WriteLine(s); } }; string[] str={ "charlies","nancy","alex","jimmy","selina"}; action(str); Console.ReadKey();
    /***************余下的类似上面的这种操作,最多可以接受四个传入参数********** * delegate void Action(); 无参,无返回值 * delegate void Action<T>(T1 arg1); * delegate void Action<T1,T2>(T1 arg1, T2 arg2); * delegate void Action<T1,T2,T3>T1 arg1, T2 arg2, T3 arg3); * delegate void Action<T1,T2,T3,T4>T1 arg1, T2 arg2, T3 arg3, T4 arg4); */
    #endregion
    #region Predicate ///bool Predicate<T>的用法 ///输入一个T类型的参数,返回值为bool类型 Predicate<string[]> predicate = delegate(string[] x) { var result = from p in x where p.Contains("s") select p; if (result.ToList().Count > 0) { return true; } else { return false; } }; string[] _value = { "charlies", "nancy", "alex", "jimmy", "selina" }; if (predicate(_value)) { Console.WriteLine("They contain."); } else { Console.WriteLine("They don't contain."); } Console.ReadKey();
    #endregion
    } } }

在WinForm和WPF中,利用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)(()=> { button2.Text = text.ToString(); }));

这样就显得非常的方便。

最后一个当然是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. #region 利用Action实现线程和界面交互
  53. private void AlternationUsingAction(object text)
  54. {
  55. //需要一个T类型的参数,无返回值
  56. this.Invoke(new Action<object>(delegate(object myText)
  57. {
  58. myText = text;
  59. button2.Text = text.ToString();
  60. }),text);
  61. }
  62.  
  63. private void AlternationUsingActionThread()
  64. {
  65. WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingAction);
  66. ThreadPool.QueueUserWorkItem(waitCallBack,"Action的使用");
  67. }
  68.  
  69. private void button2_Click(object sender, EventArgs e)
  70. {
  71. AlternationUsingActionThread();
  72. }
  73. #endregion
  74.  
  75. #region 利用Predicate实现线程和界面的交互
  76. private void AlternationUsingPrecidate(object text)
  77. {
  78. //需要一个T类型的参数,返回bool类型
  79. this.Invoke(new Predicate<object>(delegate(object myText)
  80. {
  81. myText = text;
  82. button3.Text = myText.ToString();
  83. return true; //返回值
  84. }),text);
  85. }
  86.  
  87. private void AlternationUsingPrecidateThread()
  88. {
  89. WaitCallback waitCallBack = new WaitCallback(this.AlternationUsingPrecidate);
  90. ThreadPool.QueueUserWorkItem(waitCallBack,"Predicate的使用");
  91. }
  92.  
  93. private void button3_Click(object sender, EventArgs e)
  94. {
  95. AlternationUsingPrecidateThread();
  96. }
  97. #endregion
  98.  
  99. }
  100. }

那么,现在对于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. }

逐个点击界面上的按钮,我们可以看到成功实现了线程和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函数。由于二者操作比较耗时,为了防止用户界面阻塞,我们放到线程池中:

  1. 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方式实现的异步调用,有兴趣可以参见我的这篇文章中提到的软件:

下面是软件截图:

另外在写这篇文章的时候,我在StackOverFlow上面有过提问,就是关于当前的Thread的ThreadId为什么一致的问题, 应该说两个函数放到了ThreadPool中,结果出来的ThreadId应该不一样才对呀.

其实,正确的答案是我的打印出ThreadId的信息都放在了label1.Dispatcher.Invoke这句话中,而这个Lable1属于界面UI,也就是前台线程,所以ThreadId会是一样的,如果你直接在进入函数的时候,输出ThreadId,你就会发现两个函数运行在不同的线程上了.

本文中涉及到的源码,可以从这里下载

浅谈C#中常见的委托<Func,Action,Predicate>(转)的更多相关文章

  1. 浅谈C#中常见的委托

    一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的. 关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微,尤其是张子阳的那一篇.我就不 ...

  2. [转]浅谈C#中常见的委托

    一提到委托,浮现在我们脑海中的大概是听的最多的就是类似C++的函数指针吧,呵呵,至少我的第一个反应是这样的. 关于委托的定义和使用,已经有诸多的人讲解过,并且讲解细致入微,尤其是张子阳的那一篇.我就不 ...

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

    今天我要说的是C#中的三种委托方式:Func委托,Action委托,Predicate委托以及这三种委托的常见使用场景. Func,Action,Predicate全面解析 首先来说明Func委托,通 ...

  4. 浅谈C#中Tuple和Func的使用

    为什么将Tuple和Func混合起来谈呢? 首先,介绍一下:Tuple叫做元组,是.Net Framwork4.0引入的数据类型,用来返回多个数值.在C# 4.0之前我们函数有多个返回值,通常是使用r ...

  5. 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

    浅谈 .NET 中的对象引用.非托管指针和托管指针   目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...

  6. 【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)

    [分析]浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang) 今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间 ...

  7. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  8. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  9. 浅谈HTTP中GET、POST用法以及它们的区别

    浅谈HTTP中GET.POST用法以及它们的区别 HTTP定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE.URL全称是资源描述符.我们可以这样认为: 一 ...

随机推荐

  1. Java_File类讲解_打印目录树状结构_递归算法

    package cn.xiaocangtian.testFile; import java.io.File; public class FileTree { public static void ma ...

  2. 【转】arm 开发工具比较(ADS vs RealviewMDK vs RVDS)

      ADS REALVIEW MDK RVDS 公司 ARM Keil(后被ARM收购) ARM 版本 最新1.2 ,被RVDS取代 最新4.0 是否免费 破解情况 有 有 工程管理 CodeWarr ...

  3. Linux内核源代码获取教程

    Linux内核源代码获取方法 什么叫Linux 什么叫Linux内核 Linux内核源代码的获取 什么叫Linux? Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UN ...

  4. Hbuilder快捷键

    Hbuilder编辑器功能挺强大,体积相对来说比较小,下面是一些常用到的快捷键,尽快熟练使用,成为不用鼠标的大神!哈哈哈!!! alt+↓ 跳转到下一个可编辑区: ctrl+enter 向下换行: c ...

  5. jquery轮播图详解,40行代码即可简单解决。

    我在两个月以前没有接触过html,css,jquery,javascript.今天我却在这里分享一篇技术贴,可能在技术大牛面前我的文章漏洞百出,也请斧正. 可以看出来,无论是div+css布局还是jq ...

  6. Git 简介

    版本控制 什么是版本控制? 我需要版本控制吗? - 如果你还没使用过版本控制系统,或许你会有以上疑问,甚至更多疑问.希望后面的回答能让你喜欢上版本控制系统,喜欢上Git. 什么是版本控制:顾名思义,版 ...

  7. BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分

    [题目分析] 整体二分显而易见. 自己YY了一下用树状数组区间修改,区间查询的操作. 又因为一个字母调了一下午. 貌似树状数组并不需要清空,可以用一个指针来维护,可以少一个log 懒得写了. [代码] ...

  8. import sun.net.www.MimeTable报错

    我原以为是要导什么jar包,仔细一看是 Access restriction: The type * is not accessible due to restriction on required ...

  9. dedecms 相关文章likearticle

    标签名称:likearticle 功能说明:自动关连文档标签 适用范围:内容页使用 基本语法: {dede:likearticle row='' col='' titlelen='' infolen= ...

  10. ssh自动输入密码脚本 切换目录脚本

    利用expect的,首先查看expect,命令:which expect #!/usr/bin/expect -f spawn ssh 用户名@ip地址 expect "assword:&q ...