1

私はロックについて読みましたが、まったく何も理解していませんでした。私の質問は、なぜ未使用のものを使用してobjectそれをロックするのか、そしてこれがどのようにスレッドセーフになるのか、またはこれがマルチスレッドでどのように役立つのかということです。スレッドセーフなコードを作成する他の方法はありませんか。

public class test {
    private object Lock { get; set; }

    ...
    lock (this.Lock) { ... }
    ...
}

申し訳ありませんが、私の質問は非常に愚かですが、何度も使用したことがありますが、わかりません。

4

6 に答える 6

3

他のスレッドが変更しているときに1つのスレッドからデータにアクセスすることは、「データ競合状態」(または単に「データ競合」)と呼ばれ、データの破損につながる可能性があります。(*)

ロックは、データの競合を回避するための単なるメカニズムです。2つ(またはそれ以上)の同時スレッドが同じロックオブジェクトをロックする場合、それらはもはや同時ではなく、ロックの期間中、データ競合を引き起こすことはありません。基本的に、共有データへのアクセスをシリアル化します。

秘訣は、データの競合を回避するために必要なだけロックを「広く」保ちながら、同時実行によってパフォーマンスを向上させるためにできる限り「狭く」保つことです。これは、どちらの方向にも簡単に抜け出すことができる微妙なバランスです。そのため、マルチスレッドプログラミングは困難です。

いくつかのガイドライン:

  • すべてのスレッドがデータを読み取っているだけで、誰もデータを変更しない限り、ロックは不要です。
  • 逆に、少なくとも1つのスレッドがある時点でデータを変更する可能性がある場合は、データを読み取るだけのスレッドであっても、同じデータにアクセスする すべての同時コードパスをロックを介して適切にシリアル化する必要があります。
    • 一方のコードパスでロックを使用し、もう一方のコードパスではロックを使用しないと、データは競合状態に対して広く開かれたままになります。
    • また、1つのコードパスで1つのロックオブジェクトを使用しますが、の(同時)コードパスで別のロックオブジェクトを使用すると、これらのコードパスがシリアル化されず、データの競合が発生する可能性が高くなります。
    • 一方、2つの同時コードパスが異なるデータにアクセスする場合、それらは異なるロックオブジェクトを使用できます。ただし、複数のロックオブジェクトがある場合は、デッドロックに注意してください。デッドロックは、多くの場合、「コードの競合状態」(およびheisenbug、以下を参照)でもあります。
  • ロックオブジェクトは、保護しようとしているデータと同じである必要はありません(通常はそうではありません)。残念ながら、どのデータがどのロックオブジェクトによって保護されているかを「宣言」できる言語機能がないため、コードを管理している可能性のある他の人と自分自身の両方に対して、「ロック規則」を非常に注意深く文書化する必要があります(少し経っても、ロックの慣習の隅々まで忘れてしまうからです)
  • 通常、ロックオブジェクトをできるだけ外界から保護することをお勧めします。結局のところ、ロックという非常にデリケートなタスクに使用しているので、予期しない方法で外部のアクターによってロックされたくないのです。そのため、thisまたはパブリックフィールドをロックオブジェクトとして使用することは、通常はお勧めできません。
  • キーワードは、 Monitor.EnterおよびMonitor.Exitlockより便利な構文です。
  • ロックオブジェクトは.NET内の任意のオブジェクトにすることができますが、 値オブジェクトはへの呼び出しでボックス化Monitor.Enterされます。つまり、スレッドは同じロックオブジェクトを共有せず、データは保護されません。したがって、参照型のみをロックオブジェクトとして使用してください。
  • プロセス間通信には、グローバルミューテックスを使用できます。グローバルミューテックスは、空でないnameものをミューテックスコンストラクターに渡すことで作成できます。グローバルミューテックスは、個別のプロセス間で共有できることを除いて、通常の「ローカル」ロックと基本的に同じ機能を提供します。
  • セマフォ、条件変数、メッセージキュー、不可分操作など、ロック以外の同期メカニズムがあります。異なる同期メカニズムを混在させる場合は注意してください。
  • ロックはメモリバリアとしても機能します。これは、最新のマルチコア、マルチキャッシュCPUでますます重要になっています。これは、書き込みだけでなく、データの読み取りにロックが必要な理由の一部です。

(*)並行スレッドが共有データに対して操作を実行するために「競争」しており、その競争に勝った人が操作の結果を決定するため、「競争」と呼ばれます。したがって、結果は実行のタイミングに依存します。これは、最新のプリエンプティブマルチタスクOSでは本質的にランダムです。さらに悪いことに、デバッガーなどのツールを使用してプログラムの実行を監視するという単純な行為によってタイミングを簡単に変更できるため、それらは「特異なバグ」になります(つまり、観察される現象は単なる観察行為によって変化します)。

于 2012-04-14T13:56:31.003 に答える
1

Lockオブジェクトは、1回に1人のゲストしか入室できないシングルルームへのドアのようなものです。部屋はあなたのデータにすることができ、ゲストはあなたの機能にすることができます。

  • データを定義する(部屋)
  • ドアを追加(オブジェクトをロック)
  • ゲストを招待する(機能)
  • lock指示開閉ドアを使用して、一度に1人のゲストのみが部屋に入ることができるようにします。

なぜこれが必要なのですか?同時にファイルにデータを書き込む場合(ほんの一例ですが、他の1000の場合もあります)、関数のアクセス(ゲストのドアを閉じる/開く)を書き込みファイルに同期する必要があるため、すべての関数が最後に追加されますファイルの(この例の要件であると想定)

これは当然、スレッドを同期する方法であるだけでなく、他にもあります。

  • モニター
  • ハドラーを待つ..。

それぞれの完全な情報と説明については、リンクを確認してください

スレッドの同期

于 2012-04-14T09:51:25.093 に答える
1

このlockステートメントは、相互排除の概念を紹介しています。特定のオブジェクトのロックを一度に取得できるのは1つのスレッドだけです。これにより、スレッドが共有データ構造に同時にアクセスして、それらが破損するのを防ぎます。

他のスレッドがすでにロックを保持している場合、lockステートメントは、引数の排他ロックを取得できるようになるまでブロックしてから、ブロックの実行を許可します。

lock唯一のことは、コードのブロックへのエントリを制御することであることに注意してください。クラスのメンバーへのアクセスは、ロックとはまったく関係ありません。lock同期する必要のあるアクセスが、または他の同期プリミティブを使用して調整されるようにするのは、クラス自体の責任です。また、一部またはすべてのメンバーへのアクセスを同期する必要がない場合があることにも注意してください。たとえば、カウンターを維持したい場合は、ロックせずにInterlockedクラスを使用できます。


ロックの代わりに、ロックフリーのデータ構造があります。これは、複数のスレッドが存在する場合に正しく動作します。ロックフリーデータ構造の操作は、通常、コンペアアンドスワップ(CAS)などのロックフリープリミティブを使用して、非常に慎重に設計する必要があります。

このような手法の一般的なテーマは、データ構造に対してアトミックに操作を実行し、他のスレッドによる同時アクションとそれに続く再試行が原因で操作が失敗したことを検出することです。これは、障害が発生する可能性が低い軽負荷のシステムではうまく機能しますが、障害率が上昇して再試行が主要な負荷になると、暴走動作を引き起こす可能性があります。この問題は、再試行率を下げて負荷を効果的に抑えることで改善できます。


より洗練された代替手段は、ソフトウェアトランザクショナルメモリです。CASとは異なり、STMは失敗と再試行の概念を任意に複雑なメモリ操作に一般化します。簡単に言うと、トランザクションを開始し、すべての操作を実行して、最後にコミットします。システムは、現在のスレッドをパンチに打ち負かす他のスレッドによって実行される操作の競合が原因で操作が成功しないかどうかを検出します。このような場合、STMは完全に失敗し、アプリケーションに修正措置を講じる必要があります。または、より高度な実装では、STMは自動的にトランザクションの開始に戻り、再試行できます。

于 2012-04-14T09:49:47.257 に答える
1

異なるスレッドが同じ変数/リソースに同時にアクセスしている場合、それらはこの変数/リソースに上書きする可能性があり、予期しない結果が生じる可能性があります。ロックは、一度に1つのスレッドのみが変数を評価できることを確認し、ロックが解除されるまで、スレッドはこの変数/リソースへのアクセスを取得するためにキューに入れられます。

アカウントのバランス変数があるとします。2つの異なるスレッドが100であった値を読み取ります。最初のスレッドが100+50のように50を追加して保存すると、バランスは150になります。2番目のスレッドはすでに100を読み取り、その間を意味します。100-50のように50を引くと仮定しますが、ここで注意すべき点は、最初のスレッドがバランスを150にしたため、2番目のスレッドが150-50になるはずであり、これは深刻な問題を引き起こす可能性があります。

したがって、lockは、スレッド上でいくつかのリソース状態を変更したいときに、それをロックし、変更をコミットした後に終了することを確認します

于 2012-04-14T09:50:08.697 に答える
1

はい、確かに別の方法があります:

using System.Runtime.CompilerServices;

class Test
{
    private object Lock { get; set; }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Foo()
    {
        // Now this instance is locked
    }
}

より「自然」に見えますが、オブジェクトがこのようにロックされているため、あまり使用されません。そのため、他のコードがこのオブジェクトをロックするリスクを冒すことはできません。デッドロックが発生する可能性があります。

このため、通常、オブジェクトを参照する(遅延初期化された)プライベートフィールドを作成し、代わりにそのオブジェクトをロックとして使用します。これにより、他の誰もあなたと同じオブジェクトに対してロックできないことが保証されます。


ボンネットの下で何が起こっているかについてもう少し詳しく説明します。

「オブジェクトをロックする」とは、オブジェクト自体をロックすることではありません。むしろ、プログラム全体で、メモリ内の一意であることが保証されたアドレスとしてオブジェクトを使用しています。「ロック」すると、ランタイムはオブジェクトのアドレスを取得し、それを使用して別のテーブル(非表示)内の実際のロックを検索し、そのオブジェクトを「ロック」(「クリティカルセクション」とも呼ばれます)として使用します。 ")。

つまり、実際には、オブジェクトは単なるプロキシ/シンボルであり、それ自体では何も実行していません。これは、同じプログラム内の別の有効なオブジェクトと衝突することのない一意のインジケーターとして機能しているだけです。

于 2012-04-14T09:50:17.457 に答える
1

lockあなたの混乱は、C#のキーワードに慣れたばかりの人にとってはかなり典型的なものです。そうです、lockステートメントで使用されるオブジェクトは、実際にはクリティカルセクションを定義するトークンにすぎません。そのオブジェクトは、決してマルチスレッドアクセス自体から保護されていません。

これが機能する方法は、CLRが同期ブロックと呼ばれるオブジェクトヘッダー(タイプハンドル)に4バイト(32ビットシステム)のセクションを予約することです。同期ブロックは、実際のクリティカルセクション情報を格納する配列へのインデックスにすぎません。キーワードを使用するlockと、CLRはそれに応じてこの同期ブロック値を変更します。

このスキームには長所と短所があります。利点は、クリティカルセクションを定義するためのかなり洗練されたソリューションになったことです。明らかな欠点の1つは、各オブジェクトインスタンスに同期ブロックが含まれており、ほとんどのインスタンスが同期ブロックを使用しないため、ほとんどの場合、スペースの無駄に見えることです。もう1つの欠点は、ボックス化された値型を使用できることです。これはほとんどの場合間違っており、間違いなく混乱を招きます。

lock.NETが最初にリリースされたとき、キーワードが言語にとって良いか悪いかについて多くのおしゃべりがあったことを覚えています。(少なくとも私が覚えているように)一般的なコンセンサスは、usingキーワードを代わりに簡単に使用できたので、それは悪いことでした。実際、usingキーワードを使用したソリューションは、同期ブロックを必要とせずに実行できたため、実際にはより理にかなっています。lockc#の設計チームは、キーワードがその言語に組み込まれることはなかったであろうという2回目のチャンスが与えられたとしたら、記録に残りました。1


1これについて私が見つけた唯一の参考資料は、ここのJonSkeetのWebサイトにあります。

于 2012-04-15T02:34:20.193 に答える