2

外部の.exeと通信するクラスがあります。このクラスには、同様のメソッドがたくさんあります。.exeの関数を呼び出し、応答を待ってから、trueまたはfalseを返します。

応答は、このクラスのフィールドの値を変更するイベントの形式で送信されます。

簡略化されたコード:

class Manager
{
    private static bool connected = false;

    public static bool Connect()
    {
        runtime.Connect();

        int secondsWaited = 0;

        while (!connected)
        {
            Thread.Sleep(1000);

            if (secondsWaited++ == 10)
            {
                return false;
            }
        }
        return true;
    }
}

他のメソッドは、同じcall-wait-loop-return構造を使用します。

私の目標は、次のように、私を待っているこれを行うための単一のメソッドを作成することです。

private static bool WaitReferenceEqualsValue<T>(ref T reference, T value)
{
    int secondsWaited = 0;

    while (!reference.Equals(value))
    {
        Thread.Sleep(1000);

        if (secondsWaited++ == 10)
        {
            return false;
        }
    }
    return true;
}

次に、各メソッドは次のことを行います。

runtime.DoSomething();

return WaitReferenceEqualsValue<someType>(ref someField, someSuccessfulValue);

ただし、待機ループをこのメソッド呼び出しに置き換えると、「接続された」フィールドは、参照として渡されたとしても、常に同じままです。

ここで何が起こっているのか、そして必要な機能を取得する方法について何か考えはありますか?

前もって感謝します。

編集:

public static bool Connect()
{
    ...
    runtime.Connect();

    // this code works
    /*int secondsWaited = 0;

    while (connected != true)
    {
        Thread.Sleep(1000);

        if (secondsWaited++ == 10)
        {
            return false;
        }
    }*/

    // this somehow blocks OnConnect from firing, so connected never gets set to true
    lock (typeof(SkypeKitManager))
    {
        WaitReferenceEqualsValue<bool>(ref connected, true);
    }
    ...
}

OnConnect:

private static void OnConnect(object sender, Events.OnConnectArgs e)
{
    if (e != null && e.success)
    {
        lock (typeof(Manager))
        {
            connected = true;
        }
    }
}
4

2 に答える 2

3

複数のスレッドからアクセスし、そのうちの1つが書き込み中ですが、そのフィールドで同期を行っていません。これはレースです(例外ではありません!安全に見えてもレースです。安全ではありません)。

おそらくJITはそれを登録しましたが、これは一般的な最適化です。メモリから読み取られることはなく、常にレジスタから読み取られます。同期を追加します (たとえば、ロック、Interlocked または Volatile メソッド)。

ref の使用法は正しいです。

于 2013-03-07T19:59:11.187 に答える
2

コードの問題は、本質的にコンパイラの最適化です。Fo最適化目的のコンパイラー(またはjits)は、必然的にほぼシングルスレッドのビューを取ります。コンパイラ/jitは、コードにまったく触れていないことに気付くreferenceため、比較をループの外に移動できます。基本的に競合状態を作成するため(同期/アトミックアクセスなし)、これは自由に行うことができます。

これを修正するには、同期メカニズムを使用するか、にvolatile指定子を追加しreferenceて、メソッドの外部から変数を変更できることをコンパイラー/jitに通知します。

于 2013-03-07T20:01:20.177 に答える