誰かが条件同期について説明してくれませんか?
例(できればC#で)も大歓迎です。
あなたの教授がスレッド化について話しているようです。スレッド化により、コンピュータ プログラムは一度に複数のことを実行できます。コンピューター プログラマーは、スレッドが既に実行されているときに新しいスレッドを開始することを「スレッドをスピンアップする」と呼びます。
スレッドは同じメモリ空間を共有できます。条件同期 (または単に同期) は、メモリの領域が 2 つの異なるスレッドによって同時に変更されるのを防ぐメカニズムです。
あなたが買い物に出かけていて、妻が家にいて請求書を払っているとしましょう。これは単純な例であり、実際にはこのようには機能しませんが、簡単な例として役立ちます。
あなたの妻はオンラインで請求書を支払っています。同時に、食料品店でクレジットカードをスワイプしています。どちらの行為も、当座預金口座からお金を移動することを伴います。このアクティビティをシミュレートするために、次のコードを記述します。
public class MyBanking
{
static double myAccountBalance;
//
public void DebitAccount(double debitAmount)
{
Console.Writeline("Your Old Balance is: " + myAccountBalance.ToString());
Console.Writeline("Your Debit is: " + debitAmount.ToString());
myAccountBalance = myAccountBalance - amount;
Console.Writeline("Your New Balance is: " + myAccountBalance.ToString());
}
}
仮説として、あなたの妻はあるスレッドでこのクラスの 1 つのインスタンス (「コピー」) を実行しており、あなたは別のスレッドでインスタンスを実行しています。myAccountBalance 変数は、実行中の両方のインスタンス間で共有できるように静的であると宣言されています (あなたとあなたの妻は当座預金口座を 1 つしか持っていません)。
次のようにコードを呼び出して、引き落としを行います。
MyBanking bankingObject = new MyBanking();
bankingObject.DebitAccount(100);
あなたの奥様は同時に引き落としを行います:
MyBanking bankingObject = new MyBanking();
bankingObject.DebitAccount(50);
古い残高が画面に表示された後、新しい残高が表示される前に、妻のスレッドによってスレッドが中断された場合はどうなりますか? あなたの妻のスレッドは口座から引き落とし、制御をあなたのスレッドに戻します。あなたの奥さんは画面にこれを見ます:
Your Old Balance is: 2000
Your Debit is: 50
Your New Balance Is: 1950
コンピューターがあなたの画面に新しい残高を表示するとき、あなたの奥さんの借方も数えられているので、それは間違っています。次のようなものが表示されます。
Your Old Balance is: 2000
Your Debit is: 100
Your New Balance Is: 1850
これを修正するために、メソッド コードを lock ステートメントで囲みます。lock ステートメントにより、他のすべてのスレッドがインスタンスの終了を待機します。新しいコードは次のようになります。
public class MyBanking
{
static double myAccountBalance;
//
public void DebitAccount(double debitAmount)
{
lock (this)
{
Console.Writeline("Your Old Balance is: " + myAccountBalance.ToString());
Console.Writeline("Your Debit is: " + debitAmount.ToString());
myAccountBalance = myAccountBalance - amount;
Console.Writeline("Your New Balance is: " + myAccountBalance.ToString());
}
}
}
あなたの妻のスレッドは、あなたの妻のコードが実行を開始する前に、lock ステートメント内のコードの実行が終了するのを待ちます。あなたの取引が完了している間にあなたの妻のスレッドが残高を変更する可能性がもはやないので、あなたの新しい残高は今正しいでしょう. 画面には、次のように表示されます。
Your Old Balance is: 2000
Your Debit is: 100
Your New Balance Is: 1900
あなたの妻はこれを見るでしょう:
Your Old Balance is: 1900
Your Debit is: 50
Your New Balance Is: 1850
これが同期です。
基本的には必要なスレッドのデザインパターンです
a) リソースへのアクセスを同期する
b)特定の条件が満たされるまで、他のスレッドを待つことがあります
これを C# のコンテキストで尋ねると、.NET は Monitor.Wait と Monitor.Pulse (および WaitEventhandle などのさまざまな Win32 オブジェクトのラッパー) でこれをサポートします。
SOでの素敵なキューの例を次に示します。
主な技術的詳細は次のようになります。
lock(buffer) // is Monitor.Enter(buffer) ... finally Monitor.Leave(buffer)
{
while (buffer.Count < 1)
{
Monitor.Wait(buffer);
}
...
}
そこに Wait-while-locked があることに注目してください。デッドロックのように見えますが、Wait は待機中にロックを解放します。内部のコードは、lock() { }
実行時にバッファに排他的にアクセスできます。
そして、別のスレッドがバッファに何かを入れるときにシグナルを送る必要があります。
Monitor.Pulse(buffer);
上記のコードはほぼ正しいですが、実際には間違っています。を使用lock(this)
すると、クラスのインスタンスをロックするだけでMyBanking
、妻は自分のインスタンスをロックします。共有変数へのアクセスをロックするには、型をロックする (つまりlock(typeof(MyBanking))
) か、新しい共有変数を導入してそれをロックします (int をロックできないため、通常は次のようにオブジェクトを作成します。
class MyBanking
{
static object lockObj = new object();
static double myAccountBalance;
public void DebitAccount(double debitAmount)
{
lock (lockObj)