• 多线程之共享资源


    1.lock

       Lock锁定一段代码

       lock 确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

       通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 

       违反此准则:

    • 如果实例可以被公共访问,将出现 lock (this) 问题。
    • 如果 MyType 可以被公共访问,将出现 lock(typeof (MyType)) 问题。

       由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。最佳做法是定义 private 对象来锁定, private static 对象变量来保护所有实例所共有的数据。

    2.Monitor 

      Monitor用来锁定一个对象

      Monitor具有以下的功能:

    • 与某个对象关联。
    • 它是未绑定的,可以直接从上下文中引用。
    • 不能创建Monitor实例

     将为每个同步对象维护以下信息:

    • 对当前持有锁的线程的引用。
    • 对就绪队列的引用,它包含准备获取锁的线程。
    •  对等待队列的引用,它包换正在等待锁定对象状态变化通知的线程。

    Code:   

     1 class MonitorSample
     2     {
     3         const int MAX_LOOP_TIME = 1000;
     4         Queue mQueue;
     5 
     6         public MonitorSample()
     7         {
     8             mQueue = new Queue();
     9         }
    10         public void FirstThread()
    11         {
    12             int counter = 0;
    13             lock (mQueue)
    14             {
    15                 while (counter < MAX_LOOP_TIME)
    16                 {
    17                     Monitor.Wait(mQueue);
    18                     mQueue.Enqueue(counter);
    19                     Console.WriteLine("Write:{0}", counter);
    20                     Monitor.Pulse(mQueue);
    21                     counter++;
    22                 }
    23             }
    24         }
    25         public void SecondThread()
    26         {
    27             lock (mQueue)
    28             {
    29                 Monitor.Pulse(mQueue);
    30                 while (Monitor.Wait(mQueue, 1000))
    31                 {
    32                     int counter = (int)mQueue.Dequeue();
    33                     Console.WriteLine("Read:{0}",counter);
    34                     Monitor.Pulse(mQueue);
    35                 }
    36             }
    37         }
    38         public int GetQueueCount()
    39         {
    40             return mQueue.Count;
    41         }
    42 
    43         static void Main(string[] args)
    44         {
    45             MonitorSample test = new MonitorSample();
    46             Thread tFirst = new Thread(new ThreadStart(test.FirstThread));
    47             Thread tSecond = new Thread(new ThreadStart(test.SecondThread));
    48             tFirst.Start();
    49             tSecond.Start();
    50             tFirst.Join();
    51             tSecond.Join();
    52             Console.WriteLine("Queue Count = " + test.GetQueueCount().ToString());
    53             Console.ReadKey();
    54         }
    55     }
    View Code

       上面的例子FirstThread方法首先写入一个值,SecondThead方法处于等待状态,直到收到Monitor.Pulse信号读出值,FirstThread再等待Monitor.Pulse信号写入值。

    3.Mutex.

      当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。 Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。 如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。

      可以使用 WaitHandle.WaitOne 方法请求互斥体的所属权。 拥有互斥体的线程可以在对 WaitOne 的重复调用中请求相同的互斥体而不会阻止其执行。 但线程必须调用 ReleaseMutex 方法同样多的次数以释放互斥体的所属权。 Mutex 类强制线程标识,因此互斥体只能由获得它的线程释放。 相反,Semaphore 类不强制线程标识。

       Code:

     1 class Test
     2 {
     3     private static Mutex mut = new Mutex();
     4     private const int numIterations = 1;
     5     private const int numThreads = 3;
     6 
     7     static void Main()
     8     {
     9         for (int i = 0; i < numThreads; i++)
    10         {
    11             Thread myThread = new Thread(new ThreadStart(MyThreadProc));
    12             myThread.Name = String.Format("Thread{0}", i + 1);
    13             myThread.Start();
    14             Console.ReadKey();
    15         }
    16     }
    17 
    18     private static void MyThreadProc()
    19     {
    20         for (int i = 0; i < numIterations; i++)
    21         {
    22             UseResource();
    23         }
    24     }
    25     private static void UseResource()
    26     {
    27         mut.WaitOne();
    28         Console.WriteLine("{0} has entered the protected area",
    29             Thread.CurrentThread.Name);
    30         Thread.Sleep(500);
    31         Console.WriteLine("{0} is leaving the protected area
    ",
    32             Thread.CurrentThread.Name);
    33         mut.ReleaseMutex();
    34     }
    35 }
    View Code

    4.Semaphore 

      限制可同时访问某一资源或资源池的线程数。

      使用 Semaphore 类可控制对资源池的访问。 线程通过调用 WaitOne 方法(从 WaitHandle 类继承)进入信号量,并通过调用 Release 方法释放信号量。

      信号量的计数在每次线程进入信号量时减小,在线程释放信号量时增加。 当计数为零时,后面的请求将被阻塞,直到有其他线程释放信号量。 当所有的线程都已释放信号量时,计数达到创建信号量时所指定的最大值。

    被阻止的线程并不一定按特定的顺序(如 FIFO 或 LIFO)进入信号量。

      信号量分为两种类型:局部信号量和已命名的系统信号量。 如果您使用接受名称的构造函数创建 Semaphore 对象,则该对象与具有该名称的操作系统信号量关联。 已命名的系统信号量在整个操作系统中都可见,可用于同步进程活动。 您可以创建多个 Semaphore 对象来表示同一个已命名的系统信号量,也可以使用 OpenExisting 方法打开现有的已命名系统信号量。

      下面的代码示例创建一个最大计数为 3、初始计数为 0 的全局信号量。 此示例启动五个线程,这些线程都将阻止等待该信号量。 主线程使用 Release(Int32) 方法重载,以便将信号量计数增加为其最大值,从而允许三个线程进入该信号量。 每个线程都使用 Thread.Sleep 方法等待一秒钟以便模拟工作,然后调用 Release() 方法重载以释放信号量。 每次释放信号量时,都显示前一个信号量计数。 控制台消息对信号量的使用进行跟踪。 每个线程的模拟工作间隔都稍有增加,以使输出更为易读。

     

     1 public class Example
     2 {
     3     public static Semaphore _pool;
     4     private static int _padding;
     5 
     6     public static void Main()
     7     {
     8         ExampleTest my = new ExampleTest(); //实例化。声明全局信号量mySemaphore
     9         _pool = Semaphore.OpenExisting("mySemaphore");
    10         for (int i = 1; i <= 5; i++)
    11         {
    12             Thread t = new Thread(new ParameterizedThreadStart(Worker));
    13             t.Start(i);
    14         }
    15         Thread.Sleep(500);
    16         Console.WriteLine("Main thread calls Release(3).");
    17         _pool.Release(3);
    18         Console.WriteLine("Main thread exits.");
    19         Console.ReadKey();
    20     }
    21 
    22     private static void Worker(object num)
    23     {
    24         Console.WriteLine("Thread {0} begins " + "and waits for the semaphore.", num);
    25         _pool.WaitOne();
    26         int padding = Interlocked.Add(ref _padding, 100);
    27         Console.WriteLine("Thread {0} enters the semaphore.", num);
    28         Thread.Sleep(1000 + padding);
    29         Console.WriteLine("Thread {0} releases the semaphore.", num);
    30         Console.WriteLine("Thread {0} previous semaphore count: {1}", num, _pool.Release());
    31     }
    32 }
    33 
    34 public class ExampleTest
    35 {
    36     private Semaphore mySemaphore = new Semaphore(0, 3, "mySemaphore");
    37 }
    View Code

     5.Interlocked

       为多个线程共享的变量提供原子操作

     1  class MyInterlockedExchangeExampleClass
     2     {
     3         private static int usingResource = 0;
     4         private const int numThreadIterations = 5;
     5         private const int numThreads = 10;
     6         static void Main()
     7         {
     8             Thread myThread;
     9             Random rnd = new Random();
    10             for (int i = 0; i < numThreads; i++)
    11             {
    12                 myThread = new Thread(new ThreadStart(MyThreadProc));
    13                 myThread.Name = String.Format("Thread{0}", i + 1);
    14                 Thread.Sleep(rnd.Next(0, 1000));
    15                 myThread.Start();
    16             }
    17             Console.ReadKey();
    18         }
    19         private static void MyThreadProc()
    20         {
    21             for (int i = 0; i < numThreadIterations; i++)
    22             {
    23                 UseResource();
    24                 Thread.Sleep(1000);
    25             }
    26         }
    27         static bool UseResource()
    28         {
    29             if (0 == Interlocked.Exchange(ref usingResource, 1))
    30             {
    31                 Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);
    32                 Thread.Sleep(500);
    33                 Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);
    34                 Interlocked.Exchange(ref usingResource, 0);
    35                 return true;
    36             }
    37             else
    38             {
    39                 Console.WriteLine("   {0} was denied the lock", Thread.CurrentThread.Name);
    40                 return false;
    41             }
    42         }
    43 
    44     }
    View Code

    5.volatile

      volatile关键字指示一个字段可以由多个同时执行的线程修改。

      volatile变量可以实现线程安全,但其应用有限,只有其状态完全独立于其他状态时才可使用。

  • 相关阅读:
    程序员:不要自称为码农
    SpringBoot对静态资源配置
    LeetCode 572. Subtree of Another Tree(子树)
    LeetCode 437. Path Sum III(统计路径和等于sum的路径数量)
    LeetCode 112. Path Sum(判断路径和是否等于一个数)
    LeetCode 617. Merge Two Binary Trees(归并两棵二叉树)
    LeetCode 226. Invert Binary Tree(翻转二叉树)
    Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.6 的解决办法
    linux-查询某软件的安装的目录
    WebService概念解释
  • 原文地址:https://www.cnblogs.com/mandel/p/3503269.html
Copyright © 2020-2023  润新知