2016-03-03 5 views
22

事前にお詫び申し上げます:この問題は、高度なC#を学ぶことを試みている、改訂されていないハードコアのC++開発者からのものです。以下を考慮してください。C#6ですか? (Elvis op)thread safe?もしそうなら、どうですか?

if (myUserDefinedObject != null) 
{ 
    myUserDefinedObject.ToString(); 
} 

これは明らかにスレッドセーフではありません。一方、私は言う2つのチュートリアルを見た?例えば(ヌル条件演算子や 'エルビス演算子')、

myUserDefinedObject?.ToString(); 

スレッドセーフです。コンパイラがカバーの下にある[ミューテックス]ロックを覆っていない限り、私はそれがどういうことができるのか理解していません。このイディオムがスレッドセーフであれば、誰かがそれがどのように達成されているかの技術的な説明を私に指摘できますか?スレッドセーフでない場合は、実際にはそうではないと言われるリファレンスは誰にもありますか?

+3

申し訳ありませんが、コードの最初のブロックは、使用されているコンテキストと関連する変数のスコープに応じて、完全にスレッドセーフにすることができます。 –

+2

@KenWhite - 最初のブロックのアイデアは、別のスレッドがチェックの後、 '.ToString()'の前に変数を 'null'に設定してコードを失敗させることができると思います。私はスレッドセーフではないと言います。 – Enigmativity

+0

@Enigmativity:ポスターは、コードブロックがスレッドセーフではない**と言っていました**、コンテキストやスコープを知らなくても正確なステートメントではありません。ステートメントが不正確であることを指摘しました。ブロック内のコードが確実にスレッドセーフではないことは間違いです。 –

答えて

24

MSDN(強調鉱山)から:

ヌル条件メンバーにアクセスするための別の用途は、はるかに少ないコードでスレッドセーフな方法で、デリゲートを呼び出しています。古い方法は、コードを必要とし、次のように:

var handler = this.PropertyChanged; 
if (handler != null) 
    handler(…) 

新しい方法がはるかに簡単です:

PropertyChanged?.Invoke(e) 

コンパイラは唯一のPropertyChanged 1時間を評価するためのコードを生成するための新しい方法は、スレッドセーフでありますその結果を一時変数に保存します。

ここではロックはありません。ローカルの一時変数を作成することによってスレッドセーフが強制され、ヌルチェックと他の操作の間で別のスレッドがその変数を変更できなくなります。

+6

したがって、スレッドセーフな方法は、あなたが何をしたいかによって異なります。別のスレッドがDispose()を呼び出した場合、ObjectDisposedExceptionが発生する可能性がありますが、コールサイトではnullポインタ例外は発生しません。 –

+0

誰かがC#でメモリアドレスが非ゼロであることを確認する時間と、内容にアクセスしようとしたときに知られていない未来の時間との間で、誰かがどのようにして説明することができれば、そのアドレスの内容は変更されていませんでした。私が上で引用した記事から、アトミック、メモリモードなどを使用して、その場所をマークするために* hardware *を使用して行うことができることがわかっています。それは "コンパイラ"がやっていることですか? @ハンク、あなたは答えを得るかもしれない - ちょうどあなたがその場所にいることを期待していたオブジェクトからではない。 ;-) –

+3

[ここで説明](https://msdn.microsoft.com/en-us/magazine/jj883956.aspx)の「スレッドの概要」を参照してください。 JITは、ローカル変数を完全に削除し、実際のフィールドに別の読み込みを導入することがあります。 –

28

私はBJ Myers(正しい)答えを明確にしたいと思います。

C#では、イベントはデリゲートタイプのフィールドと考えることができます。プロパティはプロパティタイプのフィールドと考えることができます。その値はnullでもかまいません。

if (this.SomeEvent != null) 
    this.SomeEvent(...); 

はスレッドセーフではありません:あなたは別のスレッドがそれを起動しようとしている間に一つのスレッドに変更されているイベントハンドラを持つの不幸な状況にある場合は、状況に入ることができます。値は、検査前にnullでなく、検査の後にnullであり、プログラムがクラッシュするように突然変異する可能性があります。

これを「スレッドセーフ」にする一般的な方法は、値をローカルにコピーしてローカルのnullをテストすることです。これには、ヌルの逆参照でクラッシュしないという利点があります。しかし、巧妙な開発者はまだレースがあることに気づくでしょう!シーケンスは、スレッドBにnullに設定

  • イベントハンドラが
  • は国家スレッドBに破壊されたイベントハンドラが必要とする
  • イベントハンドラが稼働スレッド上にキャッシュされ

    • 非ヌルイベントハンドラすることができスレッドAとはひどく死ぬ

    この意味で、このパターンは「スレッドセーフ」ではありません。あなたがこの不幸なポジションにいる場合はあなたは適切なスレッディングロジックが実装されていることを保証する責任がありますので、これは起こり得ません。しかし、あなたはそれをすることができます。あるスレッドでイベントハンドラを呼び出すことができ、別のスレッドでイベントを突然変異させることができれば、それを安全にするか競合状態のバグに対処するかのどちらかを支払わなければなりません。

    私は個人的にペストのようなこのような状況は避けますが、正しいマルチスレッドコードを書くには十分スマートではありません。今

    、実際の質問のよう:

    some_expression ?. ToString(); 
    

    temp = some_expression 
    temp == null ? null : temp.ToString() 
    

    と同じですが、あなたの意見では、後者のコード "スレッドセーフ" となっていますか?

  • +0

    ここに素晴らしいコメントがあります。あなたの優れた説明に感謝します。 「スレッドの安全性」は、スレッドがコードを使用する方法に完全に依存します。 –

    +0

    だから...安全ではない?またはそれは?複数のスレッドが混乱している場合、クラッシュを避けるためにロックを使用する必要がありますか? – Narvalex

    +4

    @Narvalex:どうすればいいですか? *あなたがロックを置いている場所*と*ロックがどんな競争を防いでいるかを説明してください*あなたの戦略がデッドロックを回避するためのもの*とし、あなたの解決策が正しいかどうかを判断することができます。 *スレッドセーフはプログラム全体のプロパティ*であることを忘れないでください。プログラムがスレッドセーフであるかどうかを知りたければ、プログラム全体の精度を提供する必要があります。 *それがスレッド化が難しい理由です*。あなたはレンガを持っていて、「このレンガは構造的に健全で作られているのですか?それは煉瓦ではなく家の財産です。 –

    関連する問題