web-dev-qa-db-ja.com

ラムダ式と匿名メソッドを使用したThreadPool.QueueUserWorkItem

スレッドプールの新しいスレッドに2つのパラメーターを渡すことは複雑な場合がありますが、ラムダ式と匿名メソッドを使用すると、次のことができるようです。

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変数が変更されることはありません。

これに何か問題がありますか?

別の方法は、test、s1、s2の3つのメンバーを持つ不変型を実装する新しいクラスを作成することです。これは、現時点ではメリットのない余分な作業のように思えます。

26
Scott Whitlock

これには何の問題もありません。コンパイラは基本的に、あなたが代替として説明したことを自動的に実行します。キャプチャされた変数(test、s1、s2)を保持するクラスを作成し、デリゲートインスタンスをラムダに渡します。ラムダは匿名クラスのメソッドに変換されます。言い換えれば、代替案を進めた場合、コンパイラーが生成したものと非常によく似たものになってしまいます。

16
chuckj

この特定の例では、ここで問題はありません。他のスレッドに渡した状態は完全に含まれており、どのタイプにもスレッドアフィニティの問題はありません。

4
JaredPar

それはそれを行うための素晴らしい方法です。ラムダを使用することのデメリットは見当たりません。シンプルでクリーンです。

2
Mehrdad Afshari

あなたが見ているものはクロージャと呼ばれます。 chuckjstates のように、コンパイラはコンパイル時に、クロージャの外部でアクセスされるメンバーに対応するクラスを生成しています。

あなたが心配しなければならない唯一のことはあなたがrefまたはoutパラメータを持っているかどうかです。文字列は不変ですが、文字列(または任意の変数)への参照は不変ではありません。

2
casperOne

パターンの潜在的な問題の1つは、非常にこのように一般的で安全性の低いものに拡張したいということです(スクラッチコード-動作することを期待しないでください)。

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
    }
}
2
Joel Coehoorn