2
   public Response Delete(Request fRequest)
        {
            Response fResponse = new Response();
            try
            {

                    foreach (Record rec in fRequest.Records)
                    {
                        string personid = rec.PersonId;
                        int recordId = rec.RecordId;

                        EnrollRecord record = (from lst in listEnrolledCandidates
                                            where lst.PersonId == personid && lst.RecordId      == recordId
                                            select lst).FirstOrDefault();
                      lock (lockDelete)
                      {
                        listEnrolledCandidates.Remove(record);

                        int aIndex = record.ArraryIndex;  
                        pvTemplatesArrayList.RemoveAt(aIndex)                       

                      }
                    }

                    return fResponse;

            }
            catch (Exception ex)
            {


                return null;
            }

        }

上記のソースコードでは、Removeステートメントがロックされています。これは、一度に1つのスレッドだけがこれらの3行のコードを実行できることを意味します。スレッド(たとえば「A」)がこれらの3行を実行していて、別のスレッド(たとえば「B」)が別のRequestパラメーターを使用して「Delete」メソッドを呼び出す場合、実行中にスレッドAがEnrollRecord、personid、recordidの更新された値を取得する可能性はありますかリクエストオブジェクトとしての動作が異なりますか?

4

3 に答える 3

1

'personId'と'recordId'の値は、各スレッドの実行に応じて変更されるとは思いません(これは、fRequestパラメーターが各スレッドに固有であることを前提としています)

ただし、「レコード」は奇妙なことが起こる可能性があります。値は変更されませんが、スレッドAがその値を取得し、スレッドBがリストから値を削除するため、実際にはその値は存在しなくなります。

「グローバル」または共有変数は、マルチスレッドの状況でアクセスを同期する必要があります。

リストアクセスをロック内に移動します。

于 2013-03-18T06:46:21.273 に答える
1

はい、それは完全に可能であるように思われるのでEnrollRecord record = (from lst in listEnrolledCandidates where lst.PersonId == personid && lst.RecordId == recordId select lst).FirstOrDefault();、クリティカルセクションの一部であり、複数のスレッドから保護されている必要があります。

于 2013-03-18T06:27:03.617 に答える
0
  • いいえ、スレッドBはスレッドAのrecordid andpersonid`の値を変更しません
  • いいえ、スレッドBはスレッドAの参照をに変更しませんが、両方が同じオブジェクトを参照している場合、スレッドBはオブジェクトの内部状態をrecord更新できます。EnrollRecordEnrollRecord
  • ただし、最も重要なのは、メソッドがスレッドセーフではない可能性があることです。

編集:

コメントすることは許可されていないので、他のコメントであなたの質問を明確にするために:

ただし、「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();

なんで?

私はあなたの質問をその部分に分割し、それらのそれぞれに答えようとします:

  1. ロック内で3行を実行できるのは、一度に1つのスレッドのみです。
  2. Request(fRequest)オブジェクトが異なる ため、スレッドAが操作の実行中に次の更新された値を取得する可能性はありますか?
    • EnrollRecord
    • personid
    • recordid

fRequestしたがって、「Request( )オブジェクトは異なるため」というステートメントから、次のことを意味すると想定します。Request( fRequest)オブジェクトは個別に作成されるため、スレッドAとBの間で共有される可能性のある参照ではありません。

これをまとめる:

一度に1つのスレッドだけが、ロックブロック内の3行を実行します。

これは問題ではなかったと思いますが、スレッドAがスレッドBによって更新されない理由personidを理解するのに役立つため、最初にこれを明確にすることが重要です。recordId

メソッドには任意の数のスレッドを入力できますが、メソッドがアクセスするオブジェクトのスコープに注意する必要があります。「青写真」として記述されているメソッドをほぼ想像できます。スレッドがメソッドに入ると、その青写真から物理構造が作成されます。各スレッドには独自の物理構造があります。設計図を車のデザインとして表示します。あなたと私は同じメーカー、モデル、年式、色の車を運転するかもしれませんが、私が運転している間、あなたは私の膝の上に座っていません、そしてあなたがラジオ局を変えても私のラジオ局は変わりません-私たちはそれぞれ持っています私たち自身のコピー。

あなたの例では:

  1. string personidスコープはメソッドであるため、その値はこのメソッドを呼び出すスレッドに固有です。
  2. 同じことが当てはまりますint recordId
  3. EnrollRecord recordもメソッドにスコープされているため、スレッドAで参照しているのはスレッドBでrecord更新できません。record

ただし、値型とは異なり、record参照型です。これは非常に重要です。オブジェクトへの参照です。メソッド内でスコープが設定されます。したがって、各スレッドは、同じオブジェクトまたは異なるオブジェクトへの参照を保持する場合があります。スレッドが参照を変更しても、別のスレッドには影響しませんが、参照オブジェクト内で何かを変更すると(以下の他のポイントを参照)、両方のスレッドに変更が表示されます。intrecord

これらのいずれかがメソッド変数ではなくクラスフィールドである場合、あるスレッドが値を変更したり、別のスレッドが参照したりする可能性があります。

参照型と値型

値のタイプ:スレッド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も同様です。PersonIdRecordIdrecordPersonIdRecordId

ロック

したがって、はい、一度に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プロパティを持つオブジェクトであるとしましょう)、新しい曲をリクエストすると、私が聴いている曲を変更します。(私は、私が喜んでそうする限り、そのアナロジーをとったと思います)。

この例では、各スレッドは同じオブジェクトへの個別の参照を保持しています

于 2013-03-18T10:19:52.083 に答える