0

サーバーの応答を待機し、ラムダを使用して取得したときに何かを実行するコードがあります。また、このラムダのクラスivar _timedOutをチェックして、何をすべきかを確認します。ラムダが作成された後、呼び出される前に、クラス内の別の場所で_timedOutが変更された場合、ラムダは_timedOutのどの値を参照しますか?

私はこれに対する答えを求めてSOをトロールしましたが、どの答えもこの特定のクエリに対応していないようです。コード-

public class MyClass
{
    public MyClass()
    {
        _databaseService = //...database stuff
        _uploadService = //...uploads info
        _serverService = //...gets stuff from the server

        _uploadService.UploadingStatusChanged += UploadStatusChanged; 
    }

    private bool _timedOut = false;


    private void GetFinalInfo()
    {
        FinalInfo finalInfo = _databaseService.GetFinalInfo();

        if (finalInfo == null) // still have no finalInfo
        {

            _serverService.GetLatestFinalInfo((response, theFinalInfo) =>
            {
                if (!_timedOut) // this could be changed elsewhere in the class while we're waiting for the server response
                {
                    if (response == ServerResponse.Successful)
                    {
                        _databaseService.AddFinalInfo(theFinalInfo);
                        // navigate to next screen
                    }
                    else
                    {
                        // do something else
                    }
                }
            });
        }
        else
        {
            // navigate to next screen
        }
    }

}


private void UploadStatusChanged(object s, MyEventArgs e)
{
    // do stuff & call GetFinalInfo if good
}

助けてくれてありがとう!

4

3 に答える 3

3

_timeoutラムダのクロージャーの一部になります。

ラムダの値は、呼び出されたときの値になることを意味します。

于 2012-09-24T15:02:42.990 に答える
2

ラムダが作成された後、呼び出される前に、クラス内の別の場所で_timedOutが変更された場合、ラムダは_timedOutのどの値を参照しますか?

インスタンス変数を参照することで参照を効果的にキャプチャしているため(ローカル変数をキャプチャしていないため)、ラムダ式はインスタンスメソッドに変換されます。thisラムダ式によって作成されたデリゲートのターゲットはthis。であるため、デリゲートが実行されると、への変更が「認識」され_timedOutます。

もちろん、これは通常のスレッドセーフの問題の影響を受けます。1つのスレッドが変数の値を変更した場合、追加の同期やメモリバリアなしで、別のスレッドがその変数を読み取って古い値を確認しようとする可能性があります。

于 2012-09-24T15:08:34.507 に答える
0

外部変数はキャプチャする必要があるとは見なされ_timedOutないため、「キャプチャ」されません。コンパイラが行うことは、問題のクラスでインスタンスメソッドを生成し、クロージャを作成する代わりに、ラムダ内のコードをインスタンスメソッドに効果的に「移動」することです。たとえば、コンパイラは次のMyClassようなメソッドを生成します。

[CompilerGenerated]
private void <GetFinalInfo>b__0(ServerResponse response, object theFinalInfo)
{
    if (!this._timedOut)
    {
        if (response == ServerResponse.Successful)
        {
            this._databaseService.AddFinalInfo(theFinalInfo);
        }
    }
}

ラムダのコードをエルゴすると、常に_timedOutフィールド(および_databaseServiceフィールド)に直接アクセスします。ローカル変数にアクセスした場合、コンパイラはそれらを含むクラスを生成することにより、そのローカル変数とその他の「外部変数」をキャプチャするように強制され、その時点thisでキャプチャされます。たとえば、コードを少し変更した場合:FinalInfo finalInfo = _databaseService.GetFinalInfo(); MyStruct myStruct = new MyStruct(); myStruct.i = 1;

if (finalInfo == null) // still have no finalInfo
{

    _serverService.GetLatestFinalInfo((response, theFinalInfo) =>
                                        {
                                            Trace.WriteLine(myStruct.i);
                                        if (!_timedOut) // this could be changed elsewhere in the class while we're waiting for the server response
                                        {
                                            if (response == ServerResponse.Successful)
                                            {
                                                _databaseService.AddFinalInfo(theFinalInfo);
                                                // navigate to next screen
                                            }
                                            else
                                            {
                                                // do something else
                                            }
                                        }
                                        });
}
        }

コンパイラは、GetFinalInfoでキャプチャを実行するコードを生成します。

MyClass.<>c__DisplayClass2 <>c__DisplayClass = new MyClass.<>c__DisplayClass2();
<>c__DisplayClass.<>4__this = this;
FinalInfo finalInfo = this._databaseService.GetFinalInfo();
<>c__DisplayClass.bleah = default(MyStruct);
<>c__DisplayClass.bleah.i = 1;
if (finalInfo == null)
{
    this._serverService.GetLatestFinalInfo(new Action<ServerResponse, object>(<>c__DisplayClass.<GetFinalInfo>b__0));
}

...明らかに。の「コピー」を作成しますthis。もちろん、この場合でもthis、参照にすぎないため、参照<>c__DisplayClass.<>4__thisされると、元の_timedOutファイルを直接参照します。

現在、フィールドに直接アクセスしているにもかかわらず、コンパイラは、揮発性の読み取りを介してアクセスされないため、この変数の使用を自由に最適化できます。これは、ラムダの使用とは無関係です。_timedOutここで複数のスレッドが機能するようになると、 x86/x64以外のアーキテクチャで行われたすべての書き込みがコードに表示されない場合があります。複数のスレッドを使用していないよう_timedOutであり、コンパイラが_timedOutx86 / x64上の別のスレッドでの更新を認識しないコードを生成するような方法で使用しているようには見えませんが、コードを変更していますそれを紹介することができます。

外側変数

Any local variable, value parameter, or parameter array whose scope includes the lambda-expression or anonymous-method-expression is called an outer variable of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.

Captured

When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function.

于 2012-09-24T16:30:20.533 に答える