26

将两个参数传递给线程池上的新线程有时会很复杂,但似乎使用 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。在这一点上,这似乎是额外的工作,没有任何好处。

4

5 回答 5

16

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

于 2009-04-10T16:24:39.860 回答
4

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

于 2009-04-10T16:22:42.250 回答
2

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

于 2009-04-10T16:21:43.377 回答
2

您正在查看的内容称为闭包。正如chuckj 所说,编译器在编译时生成一个类,该类对应于在闭包之外访问的成员。

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

于 2009-04-10T16:27:27.857 回答
2

该模式的一个潜在问题是,容易将其扩展为更通用但更不安全的东西(暂存代码——不要指望它能工作):

public static void QueueTwoParameterWorkItem<T1, T2>(T1 value1, T2 value2, workDelegate<T1,T2> work)
{
    try
    {
        T1 param1 = value1;
        T2 param2 = value2;
        ThreadPool.QueueUserWorkItem(
            (o) =>
            {
                work(param1, param2);
            });
    }
    catch (Exception ex)
    {
        //exception logic
    }
}
于 2009-04-10T16:28:52.703 回答