Thread同期/しきい値


🚀 同期コンセプト


マルチスレッド環境では、複数のスレッドが1つの共有データを使用するため、1つのスレッドにデータを格納する場合、別のスレッドからデータを読み込むか、反対のスレッドからデータを読み出すなど、多くの問題が発生する可能性があります.したがって、データを格納する過程では、データの読み出しを禁止しなければならず、データの読み出し過程では、データを書き込むことができない.この場合、1つのスレッドが動作中に別のスレッドに干渉されるスレッドの同期を防止することが必要である.
同期臨界領域では、スレッドがリソースを順番に使用します.

🚀 同期メソッド


🔥 Interlocked


  • C#のInterlockedは、マルチスレッド共有変数に対して原子レベル演算を提供します.

  • これは臨界領域内の固有資源変数の原子性を保証する.
  • ここでの原子性は,すべて動作するか,すべて動作しないかを保証する特性である.
    
            static int num = 0;
            static void Main(string[] args)
            {
                Task t1 = new Task(Thread1);
                Task t2 = new Task(Thread2);
                t1.Start();
                t2.Start();
    
                Task.WaitAll(t1, t2);
    
                Console.WriteLine(num);
            }
    
            static void Thread1()
            {
                for(int i = 0; i< 1000000; i ++)
                {
                    num++;
                }
            }
    
            static void Thread2()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    num--;
                }
            }
    上のコードでは、共有リソースnumにアクセスするスレッドが2つあります.最初のスレッドは百万回1増加し、2番目のスレッドは百万回1減少するため、numの値は2つのスレッドの終了時に0になる可能性があります.

    実際にはスペクトル外の値が出力されます.これは,2つのスレッドが並列に進行し,資源の原子性を保証できないためである.従って、原子性を保証するために、共有リソースnumでインターロックを使用する必要がある.
     static int num = 0;
            static void Main(string[] args)
            {
                Task t1 = new Task(Thread1);
                Task t2 = new Task(Thread2);
                t1.Start();
                t2.Start();
    
                Task.WaitAll(t1, t2);
    
                Console.WriteLine(num);
            }
    
            static void Thread1()
            {
                for(int i = 0; i< 1000000; i ++)
                {
                    Interlocked.Increment(ref num);
                }
            }
    
            static void Thread2()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    Interlocked.Decrement(ref num);
                }
            }
    
    上記のコードのように,クラスの増分と増分をロックすることで原子性が保証される.

    このときの実行結果は、出力の値が予想値と同じであることがわかります.

    🔥 Lock

  • 反発ロックを取得し、blockを実行してロックを解除します.
  • 他のスレッドでのロック取得が阻止する、ロック解除待ち
  • .
    class Program
        {
            static int Count = 0;
    
            static System.Object lockThis = new System.Object();
            
            static void Print()
            {
                lock (lockThis)
                {
                    for(int i =0; i< 5; i++)
                    {
                        Count++;
                        Console.WriteLine(Count);
                    }
                }
            }
            static void Main(string[] args)
            {
                Thread thread1 = new Thread(new ThreadStart(Print));
                Thread thread2 = new Thread(new ThreadStart(Print));
                Thread thread3 = new Thread(new ThreadStart(Print));
                Thread thread4 = new Thread(new ThreadStart(Print));
    
                thread1.Start();
                thread2.Start();
                thread3.Start();
                thread4.Start();
            }
    
        }
  • は、1つのスレッドでロック解除を待機し、別のスレッドでロック解除を待機し、ロック解除後に実行されることがわかります.

    🔥 Moniter

  • Moniterもlockと同様に、コードブロックが同時に複数のスレッド上で
  • を実行することを防止する.
    
    class Program
        {
            static int Count = 0;
    
            static System.Object moniterlock = new System.Object();
            
            static void Print()
            {
                Monitor.Enter(moniterlock);
    
                try
                {
                    for(int i = 0; i<5; i++)
                    {
                        Count++;
    
                        Console.WriteLine(Count);
                    }
                }
    
                finally
                {
                    Monitor.Exit(moniterlock);
                }
            }
            static void Main(string[] args)
            {
                Thread thread1 = new Thread(new ThreadStart(Print));
                Thread thread2 = new Thread(new ThreadStart(Print));
                Thread thread3 = new Thread(new ThreadStart(Print));
                Thread thread4 = new Thread(new ThreadStart(Print));
    
                thread1.Start();
                thread2.Start();
                thread3.Start();
                thread4.Start();
            }
    
        }
        
    Ref https://www.hanbit.co.kr/network/category/category_view.html?cms_code=CMS1669525805
    https://neohtux.tistory.com/220?category=620074
    https://scvtwo.tistory.com/67