4

私は次のコードで遊んでいます:

class RunMeBaby
{
    public void Start()
    {
        while (true)
        {
            Console.WriteLine("I'm " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(1000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        RunMeBaby r = new RunMeBaby();
        Thread t = new Thread(r.Start);  // ParameterizedThreadStart delegate
        r = null;
        GC.Collect(GC.MaxGeneration);
        t.Start();

        r = new RunMeBaby();
        t = new Thread(() => r.Start()); // ThreadStart delegate
        t.Start();
        //Thread.Sleep(1000);
        r = null;
    }
}

mainの最初の部分は問題なく実行されますが、Thread.Sleep()メソッドの呼び出しにコメントを付けると2番目の部分は失敗し、null例外が発生します。

私の理解では、ラムダ式が遅延評価されているため、新しいスレッドが十分に速く開始されず、メインスレッドが最初に設定rされる可能性があります。nullここで、この「2番目の部分」をローカルスコープを持つ静的メソッドに入れるrと、問題は解消されました。しかし、その特定のケースでは、問題がスレッドスケジューラによって隠されているかどうか、おそらく別のワークロードを持つ別のマシンで問題が発生する可能性があるかどうか疑問に思います。rまたは、ラムダ式について、スコープ外であっても、に設定されていない限り、何らかの形で参照されることを保証する何かがありnullますか。

そして最終的には、ParameterizedThreadStartデリゲートをできるだけ使用することを検討する必要があるのか​​、それとも特定の条件を尊重してラムダを有効に保つためにラムダに固執する必要があるのか​​疑問に思います。

4

2 に答える 2

6

ガベージ コレクションについて説明する前に、まず、作成したコードを理解しましょう。

以下の間には大きな違いがあります。

new Thread(r.Start)

Startの現在の値でメソッドのデリゲートを作成しますr。つまり、

new Thread(new ThreadStart(r.Start)) // identical to new Thread(r.Start)

上記のいずれかで、nowrが評価されるため、後で別のインスタンス (または null) に変更しても影響しません。対比:

new Thread(() => r.Start())

これは、変数をキャプチャrする匿名メソッドです。つまり、匿名メソッドが呼び出されたとき、つまり 2 番目のスレッドが実行されているときにrのみ評価されます。したがって、はい: の値を変更すると、異なる結果が得られる可能性が非常に高くなります (または、null に変更するとエラーになります)。r

パラメータ化されたスレッドの開始も機能します。

new Thread(state => ((RunMeBaby)state).Start(), r);

の現在の値をrパラメーター値として渡すため、修正されました。デリゲートが呼び出されると、その時点で(以前は)含まstateていた値が取得されるため、それを適切な型にキャストして安全に使用できます。r

今!ガベージ コレクションに関しては、特に知っておくべきことはありません。はい、参照を に渡すと、参照rコピーParameterizedThreadStart(オブジェクトのコピーではない)が作成されるため、ガベージ コレクションがスコープ内になくなるまで防止されます。ただし、同じことは元のアプローチにも当てはまります。注意が必要なのは「キャプチャされた変数」の例 ( ) だけですが、ここで発生している問題はGC とはまったく関係なく、すべてキャプチャされた変数のルールに関係しています。new Thread(r.Start)() => r.Start()

于 2012-04-24T09:07:32.523 に答える
2

私がすべてを理解していない多くの質問。私が言えることは、

Thread t = new Thread(r.Start)

デリゲートを通じて呼び出されるメソッド実装を提供するインスタンスを内部的に指すデリゲートを作成します。したがって、 Thread -> ParameterizedDelegate -> your method -> RunMeBabyの関係があるため、 r を null に設定しても効果はありません。

r = new RunMeBaby();
t = new Thread(() => r.Start());

r の周りにクロージャを作成しますが、r を変更することはできます。null に設定して後でアクセスすると、NREが取得されます。

于 2012-04-24T09:07:00.963 に答える