- いいえ、スレッドBはスレッドAの
recordid and
personid`の値を変更しません
- いいえ、スレッドBはスレッドAの参照をに変更しませんが、両方が同じオブジェクトを参照している場合、スレッドBはオブジェクトの内部状態を
record
更新できます。EnrollRecord
EnrollRecord
- ただし、最も重要なのは、メソッドがスレッドセーフではない可能性があることです。
編集:
コメントすることは許可されていないので、他のコメントであなたの質問を明確にするために:
ただし、「EnrollRecordレコード」は、スレッドAのコンテキストではローカルオブジェクトとして扱われます。Aには独自のコピーが含まれ、スレッドBには独自のコピーが含まれます。そうじゃない?
これはおそらくそうではなく、理解することが重要です。EnrollRecord
、私は、クラスだと思います。したがって、これは参照型です。各スレッドは、を介して同じオブジェクトを参照できますがrecord
、その参照変数record
はスレッド間で共有されません。コードが立っているので、各スレッドはの個別のコピーを作成しませんEnrollRecord
。これに対する例外は、たとえば、linqクエリによってSQLクエリが発生し、データプロバイダーがオブジェクトの複製コピーをメモリ内に作成することを許可している場合です。削除を保護するためにロックを使用しているため、これは当てはまらないと思います。これは、の一般的なメモリ内コレクションを示唆していますlistEnrolledCandidates
。
編集を終了し、元の回答を続行します。
このメソッドをスレッドセーフにするには、次の行もロックで保護する必要があります。
EnrollRecord record = (from lst in listEnrolledCandidates
where lst.PersonId == personid && lst.RecordId == recordId
select lst).FirstOrDefault();
なんで?
私はあなたの質問をその部分に分割し、それらのそれぞれに答えようとします:
- ロック内で3行を実行できるのは、一度に1つのスレッドのみです。
- Request(
fRequest
)オブジェクトが異なる
ため、スレッドAが操作の実行中に次の更新された値を取得する可能性はありますか?
EnrollRecord
personid
recordid
fRequest
したがって、「Request( )オブジェクトは異なるため」というステートメントから、次のことを意味すると想定します。Request( fRequest
)オブジェクトは個別に作成されるため、スレッドAとBの間で共有される可能性のある参照ではありません。
これをまとめる:
一度に1つのスレッドだけが、ロックブロック内の3行を実行します。
これは問題ではなかったと思いますが、スレッドAがスレッドBによって更新されない理由personid
を理解するのに役立つため、最初にこれを明確にすることが重要です。recordId
メソッドには任意の数のスレッドを入力できますが、メソッドがアクセスするオブジェクトのスコープに注意する必要があります。「青写真」として記述されているメソッドをほぼ想像できます。スレッドがメソッドに入ると、その青写真から物理構造が作成されます。各スレッドには独自の物理構造があります。設計図を車のデザインとして表示します。あなたと私は同じメーカー、モデル、年式、色の車を運転するかもしれませんが、私が運転している間、あなたは私の膝の上に座っていません、そしてあなたがラジオ局を変えても私のラジオ局は変わりません-私たちはそれぞれ持っています私たち自身のコピー。
あなたの例では:
string personid
スコープはメソッドであるため、その値はこのメソッドを呼び出すスレッドに固有です。
- 同じことが当てはまります
int recordId
EnrollRecord record
もメソッドにスコープされているため、スレッドAで参照しているのはスレッドBでrecord
更新できません。record
ただし、値型とは異なり、record
は参照型です。これは非常に重要です。オブジェクトへの参照です。メソッド内でスコープが設定されます。したがって、各スレッドは、同じオブジェクトまたは異なるオブジェクトへの参照を保持する場合があります。スレッドが参照を変更しても、別のスレッドには影響しませんが、参照オブジェクト内で何かを変更すると(以下の他のポイントを参照)、両方のスレッドに変更が表示されます。int
record
これらのいずれかがメソッド変数ではなくクラスフィールドである場合、あるスレッドが値を変更したり、別のスレッドが参照したりする可能性があります。
参照型と値型
値のタイプ:スレッドAが変数recordIdを更新する場合、スレッドBはこれを認識しません。recordIdが、メソッドレベルの変数ではなく、クラスレベル(たとえば、private int recordId)のフィールドとして宣言された場合、これは当てはまりません。どのスレッドも値に同じアクセス権を持ち、読み取りと更新の両方。これがなぜであるかを理解するには、スタックストレージとヒープストレージの違いを、可変スコープで調べる必要があります。持ち帰り:メソッド内で宣言および使用される値型は、(ほとんど)同じメソッドを実行している他のスレッドからアクセスできません。
参照タイプ
-スレッドAが(、、)が(1、1)のレコードに値を設定し、record
次にスレッドBが値を(2、2)に対応するレコードに設定した場合、スレッドAは引き続き(1、 1)。-スレッドAが値を(1、1)の( 、 )を持つレコードに設定し、次にスレッドBが値を(1、1)に対応するレコードに設定した場合、スレッドAは引き続き(1、1)を参照します。 、スレッドBも同様です。PersonId
RecordId
record
PersonId
RecordId
ロック
したがって、はい、一度に1つのスレッドのみがロック領域に入ることができますが、ローカルで宣言された変数を保護するためにロックは必要ありません。スレッドに対してローカルな変数ではなく、共有状態を保護するためにロックが必要です。そうでない場合、複数のスレッドから次のメソッドを実行して正しい答えを得ることができませんでした。
public int MinusOne(int value) {
int newValue = value - 1;
return newValue;
}
あなたの質問の最も重要なポイント
質問の最も重要な部分は、あなたが実際に尋ねなかったことです。問題は次のとおり
です。このメソッドはスレッドセーフですか
私があなたのコードを見ると、答えは次のようになります:いいえ
私はあなたの質問に答えるために、それlistEnrolledCandidates
がタイプのリストへの共有参照であると仮定しなければなりませんIEnumerable<EnrollRecord>
。また、ロックを使用してこのリストからレコードを削除しているため、スレッドセーフなコレクション/リスト/ディクショナリではないと想定する必要があります。
これは非常に重要です。スレッドAが実行している間、スレッドBがレコードを削除しようとできないようにロックしていますlistEnrolledCandidates
が、次のシナリオではどうなりますか。
シナリオ1。
- スレッドAは、
record
そのスコープ内に参照を設定します
listEnrolledCandidates
スレッドBはレコードを見つけるために列挙を開始します
- スレッドAはレコードを削除します
- スレッドBは列挙を終了します
シナリオ2。
- スレッドAは
record
、検索キー(1、1)に対応するへの参照を設定します。
- スレッドBは
record
、検索キー(1、1)に対応する参照を設定します。
- スレッドAがレコードを削除するようになりました
- スレッドBがレコードを削除するようになりました
これらの両方のシナリオで、最終的に問題が発生する可能性があります。スレッドセーフではないコレクション、リスト、ディクショナリを使用している場合は、検索、列挙、ループ、および追加、削除、更新などの両方を保護する必要があります。これらの目的のために、積極的に使用するかlock
、ReaderWriterLock(または同じのスリムバージョン)。
2番目のシナリオでは、削除ロック領域に入ったときに、レコードがまだ存在することを再度確認する必要があります。これは、削除によって、record
がコレクションに存在しないという例外がスローされた場合に備えてです。
その他のポイント
EnrollRecord
のようなプロパティがあると仮定しますpublic string EnrollYear { get; set; }
。また、スレッドAとスレッドBの両方が(1、1)の( PersonId
、 )を参照していると仮定します。RecordId
-スレッドAが新しい値に変更EnrollYear
された場合、スレッドBはこの変更を確認します。record
-スレッドAがに設定record
されているnull
場合、スレッドBはレコードをとして表示せず、レコードnull
への参照を保持していると表示されます
私たちの車の例では、私が聴いているラジオ局に電話して(RadioStationがCurrentSongプロパティを持つオブジェクトであるとしましょう)、新しい曲をリクエストすると、私が聴いている曲を変更します。(私は、私が喜んでそうする限り、そのアナロジーをとったと思います)。
この例では、各スレッドは同じオブジェクトへの個別の参照を保持しています