ThreadPool.QueueUserWorkItem,带有lambda表达式和匿名方法

将两个参数传递给线程池上的新线程有时会很复杂,但看起来使用lambda表达式和匿名方法,我可以这样做:

public class TestClass { public void DoWork(string s1, string s2) { Console.WriteLine(s1); Console.WriteLine(s2); } } try { TestClass test = new TestClass(); string s1 = "Hello"; string s2 = "World"; ThreadPool.QueueUserWorkItem( o => test.DoWork(s1, s2) ); } catch (Exception ex) { //exception logic } 

现在,我肯定简化了这个例子,但这些要点是关键:

  • 传递的字符串对象是不可变的,因此是线程安全的
  • s1和s2变量在try块的范围内声明,我在将工作排队到线程池后立即退出,因此在此之后永远不会修改s1和s2变量。

这有什么问题吗?

另一种方法是创建一个实现具有3个成员的不可变类型的新类:test,s1和s2。 这似乎是额外的工作,在这一点上没有任何好处。

这没有什么不对。 编译器本质上是自动执行您所描述的替代方案。 它创建一个类来保存捕获的变量(test,s1和s2),并将委托实例传递给lambda,而lambda将转换为匿名类的方法。 换句话说,如果您继续使用替代方案,那么最终会得到与编译器为您生成的内容非常相似的东西。

对于这个特例,这里没有任何错误。 您传递到另一个线程的状态是完全包含的,并且所涉及的类型都没有任何线程关联问题。

这是一个很好的方式。 我没有看到使用lambdas的任何缺点。 它简单而干净。

你所看到的被称为封闭。 正如chuckj所述 ,编译器在编译时生成一个类,该类对应于在闭包之外访问的成员。

你唯一需要担心的是你是否有ref或out参数。 虽然字符串是不可变的,但对它们(或任何变量)的引用不是。

该模式的一个潜在问题是,将它扩展为更通用但更不安全的东西是非常诱人的(刮擦代码 – 不要指望它能够工作):

 public static void QueueTwoParameterWorkItem(T1 value1, T2 value2, workDelegate work) { try { T1 param1 = value1; T2 param2 = value2; ThreadPool.QueueUserWorkItem( (o) => { work(param1, param2); }); } catch (Exception ex) { //exception logic } }