zoukankan      html  css  js  c++  java
  • 多线程之共享资源

    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变量可以实现线程安全,但其应用有限,只有其状态完全独立于其他状态时才可使用。

  • 相关阅读:
    mysql自动补齐
    重置oracle 11G的system、sys密码《亲测》
    细说业务逻辑(前篇)<转>
    当我们把这么一个“狭义的概念”与“这个概念本身”等同起来时,误会、迷茫、困惑、不屑就出现了。
    设计模式就三个准则
    面向对象编程(OOP)的三大特点
    在计算机中,“透明”一词的理解。
    关于ArrayList中添加对象的一个有趣问题~~~~
    MyEclipse 代码自动提示功能失效 提示No Default Proposals 或 no completions available 的解决方法
    面向对象的三大基本特征
  • 原文地址:https://www.cnblogs.com/mandel/p/3503269.html
Copyright © 2011-2022 走看看