web-dev-qa-db-ja.com

returnステートメントはロックの内側または外側にあるべきですか?

コード内のある場所で、ロックの内側と外側のどちらかにreturnステートメントがあることに気付きました。どれが最高ですか?

1)

void example()
{
    lock (mutex)
    {
    //...
    }
    return myData;
}

2)

void example()
{
    lock (mutex)
    {
    //...
    return myData;
    }

}

どちらを使用すればよいですか?

129

基本的に、どちらでもコードが簡単になります。シングルポイントの出口は理想的ですが、それを達成するためだけにコードを変形させません...そして、ローカル変数(ロック外)を宣言し、それを初期化(ロック内)し、 (ロックの外で)それを返すと、ロック内の単純な "fooを返す"の方がずっと簡単だと思います。

ILの違いを示すために、次のコードを記述できます。

static class Program
{
    static void Main() { }

    static readonly object sync = new object();

    static int GetValue() { return 5; }

    static int ReturnInside()
    {
        lock (sync)
        {
            return GetValue();
        }
    }

    static int ReturnOutside()
    {
        int val;
        lock (sync)
        {
            val = GetValue();
        }
        return val;
    }
}

ReturnInsideはC#のよりシンプルでクリーンなビットであると喜んで主張します)

そして、ILを見てください(リリースモードなど):

.method private hidebysig static int32 ReturnInside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 CS$1$0000,
        [1] object CS$2$0001)
    L_0000: ldsfld object Program::sync
    L_0005: dup 
    L_0006: stloc.1 
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0 
    L_0012: leave.s L_001b
    L_0014: ldloc.1 
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally 
    L_001b: ldloc.0 
    L_001c: ret 
    .try L_000c to L_0014 finally handler L_0014 to L_001b
} 

method private hidebysig static int32 ReturnOutside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 val,
        [1] object CS$2$0000)
    L_0000: ldsfld object Program::sync
    L_0005: dup 
    L_0006: stloc.1 
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0 
    L_0012: leave.s L_001b
    L_0014: ldloc.1 
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally 
    L_001b: ldloc.0 
    L_001c: ret 
    .try L_000c to L_0014 finally handler L_0014 to L_001b
}

そのため、ILレベルでは、それらは[与えたり、いくつかの名前を付けたり]同一です(何かを学びました;-p)。そのため、唯一の賢明な比較は、ローカルコーディングスタイルの(非常に主観的な)法則です...簡単にするためにReturnInsideを好みますが、どちらにも興奮しません。

174
Marc Gravell

違いはありません。どちらもコンパイラーによって同じものに変換されます。

明確にするために、どちらも次のセマンティクスで効果的に変換されます。

T myData;
Monitor.Enter(mutex)
try
{
    myData= // something
}
finally
{
    Monitor.Exit(mutex);
}

return myData;
37
Greg Beech

私は間違いなく戻り値をロックの中に入れます。そうしないと、別のスレッドがロックに入り、returnステートメントの前に変数を変更する危険があります。そのため、元の呼び出し元は予想とは異なる値を受け取ります。

31

外側のロックの見栄えが良いと思われる場合でも、コードを次のように変更する場合は注意してください。

return f(...)

ロックを保持した状態でf()を呼び出す必要がある場合は、明らかにロック内にある必要があります。

5
Rob Walker

場合によります、

私はここで穀物に逆らうつもりです。私は通常、ロックの内側に戻ります。

通常、変数mydataはローカル変数です。ローカル変数を初期化しながら宣言するのが好きです。ロック外で戻り値を初期化するためのデータはほとんどありません。

あなたの比較は実際には欠陥があります。理想的には、2つのオプションの違いはあなたが書いたとおりであり、ケース1にうなずくように思われますが、実際には少しいです。

void example() { 
    int myData;
    lock (foo) { 
        myData = ...;
    }
    return myData
}

vs.

void example() { 
    lock (foo) {
        return ...;
    }
}

私は、特に短いスニペットの場合、ケース2の方が読みやすく、ねじ込みにくいと感じています。

4
Edward KMETT

価値のあるものとして、 MSDNのドキュメント には、ロックの内側から戻る例があります。ここでの他の回答から、それはILにかなり似ているように見えますが、私にとっては、ロックの内側から戻る方が安全だと思われます。

1
greyseal96

仲間の開発者がコードを読みやすくするために、最初の選択肢を提案します。

1
Adam Asham

外はきれいに見えます。

0
Ovidiu Pacurar

lock() return <expression>ステートメントは常に:

1)ロックを入力します

2)指定された型の値のローカル(スレッドセーフ)ストアを作成します。

3)ストアに_<expression>_によって返された値を入力します。

4)出口ロック

5)店舗を返却します。

これは、lockステートメントから返された値が、返される前に常に「調理済み」であることを意味します。

lock() returnについて心配する必要はありません。ここでは誰も聞いてはいけません))

0
mshakurov