C# Sleep() vs Wait():到底用哪个?

打印 上一主题 下一主题

主题 1041|帖子 1041|积分 3123

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
一、引言

嘿,各位 C# 编程的小同伴们!在多线程编程的世界里,我们常常会遇到需要让线程临时 “休息” 一下的情况。这时候,Sleep()和Wait()这两个方法就如同我们的得力助手,随时预备为我们服务。但是,你真的相识它们吗?它们之间又有着怎样的区别呢?本日,就让我们一起深入探讨C#中Sleep()和Wait()的奥秘,看看在不同的场景下,究竟该如何选择,才气让我们的线程 “休息” 得恰到好处。
二、理论底子

2.1 Sleep () 原理剖析

Sleep()是System.Threading.Thread类的一个静态方法 ,它的主要作用是让当前正在执行的线程停息一段时间。其停息的时长由传入的参数决定,单元为毫秒。比方,当我们调用Thread.Sleep(2000)时,当火线程就会停息 2 秒钟。在这 2 秒内,该线程不会执行任何代码,CPU 也不会分配时间片给它。需要注意的是,Sleep()方法并不会释放当火线程所持有的锁。假设我们有一个多线程程序,其中一个线程在获取了某个对象的锁之后调用了Sleep()方法,那么在它就寝的这段时间里,其他线程是无法获取该锁的,即便它们实验访问被该锁保护的资源,也只能处于等待状态。
2.2 Wait () 原理详解

Wait()是Object类的实例方法,它用于线程间的通讯和协作。通常情况下,Wait()方法需要在同步代码块(lock语句块)或同步方法中使用,并且它依靠于一个对象锁。当一个线程调用Wait()方法后,它会释放当前持有的对象锁,并进入等待状态。此时,该线程会被放入到对象的等待队列中,直到其他线程调用了同一个对象的Notify()或NotifyAll()方法,才有可能被唤醒。比方,在一个生产者 - 消费者模子中,当消费者线程发现队列中没有数据时,可以调用Wait()方法进入等待状态,同时释放锁,让生产者线程可以或许将数据放入队列。当生产者线程完成数据生产后,调用Notify()或NotifyAll()方法唤醒等待的消费者线程。
三、深入对比

3.1 根本用法差异

Sleep()方法的使用非常简单直接,只需在需要停息线程的地方调用Thread.Sleep(毫秒数) 即可。比如,我们想要让当火线程停息 3 秒钟,可以这样写:
  1. using System;
  2. using System.Threading;
  3. class Program
  4. {
  5.     static void Main()
  6.     {
  7.         Console.WriteLine("线程开始执行");
  8.         Thread.Sleep(3000);
  9.         Console.WriteLine("线程暂停3秒后继续执行");
  10.     }
  11. }
复制代码
在上述代码中,当执行到Thread.Sleep(3000)时,当火线程会停息 3000 毫秒(即 3 秒),然后再继续执行后面的代码。
而Wait()方法的使用则相对复杂一些,它需要与lock语句共同使用,并且通常在一个循环中调用,以确保在合适的条件下等待。比方:
  1. using System;
  2. using System.Threading;
  3. class Program
  4. {
  5.     private static object _lockObject = new object();
  6.     private static bool _flag = false;
  7.     static void Main()
  8.     {
  9.         new Thread(() =>
  10.         {
  11.             lock (_lockObject)
  12.             {
  13.                 while (!_flag)
  14.                 {
  15.                     Console.WriteLine("等待条件满足...");
  16.                     Monitor.Wait(_lockObject);
  17.                 }
  18.                 Console.WriteLine("条件满足,继续执行");
  19.             }
  20.         }).Start();
  21.         new Thread(() =>
  22.         {
  23.             Thread.Sleep(2000);
  24.             lock (_lockObject)
  25.             {
  26.                 _flag = true;
  27.                 Console.WriteLine("设置条件为真,并通知等待线程");
  28.                 Monitor.Pulse(_lockObject);
  29.             }
  30.         }).Start();
  31.     }
  32. }
复制代码
在这段代码中,第一个线程在获取锁后,查抄_flag是否为true,如果不是则调用Monitor.Wait(_lockObject)进入等待状态,并释放锁。第二个线程在 2 秒后获取锁,设置_flag为true,然后调用Monitor.Pulse(_lockObject)通知等待的线程。被通知的线程重新获取锁后,继续执行后续代码。
3.2 锁处置惩罚的不同

Sleep()方法在停息线程时,不会释放当火线程所持有的锁。这意味着,如果一个线程在持有锁的情况下调用了Sleep(),其他线程想要获取该锁就必须等待该线程就寝结束并释放锁。假设有一个银行转账的场景,我们用代码模拟如下:
  1. using System;
  2. using System.Threading;
  3. class BankAccount
  4. {
  5.     private int balance = 1000;
  6.     private object lockObject = new object();
  7.     public void Withdraw(int amount)
  8.     {
  9.         lock (lockObject)
  10.         {
  11.             Console.WriteLine("开始取款操作,当前余额: " + balance);
  12.             if (amount <= balance)
  13.             {
  14.                 Thread.Sleep(2000);
  15.                 balance -= amount;
  16.                 Console.WriteLine("取款成功,剩余余额: " + balance);
  17.             }
  18.             else
  19.             {
  20.                 Console.WriteLine("余额不足,取款失败");
  21.             }
  22.         }
  23.     }
  24. }
  25. class Program
  26. {
  27.     static void Main()
  28.     {
  29.         BankAccount account = new BankAccount();
  30.         new Thread(() => account.Withdraw(500)).Start();
  31.         new Thread(() => account.Withdraw(300)).Start();
  32.     }
  33. }
复制代码
在这个例子中,Withdraw方法使用了锁来保证线程安全。当一个线程进入Withdraw方法并获取锁后,调用Thread.Sleep(2000)模拟业务处置惩罚的耗时操作,在这 2 秒内,锁不会被释放,其他线程无法进入该方法,只能等待。
与之相反,Wait()方法在调用时会释放当火线程持有的锁,使得其他线程有机会获取该锁并执行同步代码块中的内容。当等待的线程被唤醒时,它会重新实验获取锁,只有获取到锁后才会继续执行。下面是一个简单的生产者 - 消费者模子示例:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. class ProducerConsumer
  5. {
  6.     private Queue<int> queue = new Queue<int>();
  7.     private object lockObject = new object();
  8.     private const int MaxQueueSize = 5;
  9.     public void Produce(int item)
  10.     {
  11.         lock (lockObject)
  12.         {
  13.             while (queue.Count >= MaxQueueSize)
  14.             {
  15.                 Console.WriteLine("队列已满,生产者等待");
  16.                 Monitor.Wait(lockObject);
  17.             }
  18.             queue.Enqueue(item);
  19.             Console.WriteLine("生产了: " + item);
  20.             Monitor.PulseAll(lockObject);
  21.         }
  22.     }
  23.     public void Consume()
  24.     {
  25.         lock (lockObject)
  26.         {
  27.             while (queue.Count == 0)
  28.             {
  29.                 Console.WriteLine("队列已空,消费者等待");
  30.                 Monitor.Wait(lockObject);
  31.             }
  32.             int item = queue.Dequeue();
  33.             Console.WriteLine("消费了: " + item);
  34.             Monitor.PulseAll(lockObject);
  35.         }
  36.     }
  37. }
  38. class Program
  39. {
  40.     static void Main()
  41.     {
  42.         ProducerConsumer pc = new ProducerConsumer();
  43.         new Thread(() =>
  44.         {
  45.             for (int i = 1; i <= 10; i++)
  46.             {
  47.                 pc.Produce(i);
  48.                 Thread.Sleep(1000);
  49.             }
  50.         }).Start();
  51.         new Thread(() =>
  52.         {
  53.             for (int i = 1; i <= 10; i++)
  54.             {
  55.                 pc.Consume();
  56.                 Thread.Sleep(1500);
  57.             }
  58.         }).Start();
  59.     }
  60. }
复制代码
在这个例子中,当队列满时,生产者线程调用Monitor.Wait(lockObject)释放锁并进入等待状态;当队列空时,消费者线程调用Monitor.Wait(lockObject)释放锁并进入等待状态。当生产者生产了数据大概消费者消费了数据后,会调用Monitor.PulseAll(lockObject)通知等待的线程。
3.3 线程状态厘革

当线程调用Sleep()方法后,会进入壅闭状态(Blocked),在指定的时间内,该线程不会参与 CPU 的调理,也不会执行任何代码。当就寝的时间结束后,线程会从壅闭状态变化为就绪状态(Ready),等待 CPU 分配时间片来继续执行。比方,在一个游戏开发中,我们需要控制某个动画的播放速度,假设每帧动画需要停息 50 毫秒,代码如下:
  1. using System;
  2. using System.Threading;
  3. class Animation
  4. {
  5.     public void Play()
  6.     {
  7.         for (int i = 0; i < 100; i++)
  8.         {
  9.             Console.WriteLine("播放第 " + (i + 1) + " 帧动画");
  10.             Thread.Sleep(50);
  11.         }
  12.     }
  13. }
  14. class Program
  15. {
  16.     static void Main()
  17.     {
  18.         Animation animation = new Animation();
  19.         new Thread(animation.Play).Start();
  20.     }
  21. }
复制代码
在这个例子中,每播放一帧动画,线程就会调用Thread.Sleep(50)进入壅闭状态 50 毫秒,50 毫秒后进入就绪状态,等待 CPU 分配时间片继续播放下一帧。
而线程调用Wait()方法后,会进入等待状态(Waiting),并且释放持有的锁。此时,该线程会被放入对象的等待队列中,直到被其他线程通过Notify()或NotifyAll()方法唤醒。唤醒后,线程会从等待状态变化为就绪状态,竞争锁资源,获取到锁后才会继续执行。比方,在一个多线程协作的文件处置惩罚系统中,假设有一个主线程负责读取文件内容,一个辅助线程负责对读取的内容举行解析,代码如下:
  1. using System;
  2. using System.IO;
  3. using System.Threading;
  4. class FileProcessor
  5. {
  6.     private object lockObject = new object();
  7.     private string fileContent;
  8.     private bool isContentReady = false;
  9.     public void ReadFile(string filePath)
  10.     {
  11.         lock (lockObject)
  12.         {
  13.             using (StreamReader reader = new StreamReader(filePath))
  14.             {
  15.                 fileContent = reader.ReadToEnd();
  16.             }
  17.             isContentReady = true;
  18.             Console.WriteLine("文件读取完成,通知解析线程");
  19.             Monitor.Pulse(lockObject);
  20.         }
  21.     }
  22.     public void ParseContent()
  23.     {
  24.         lock (lockObject)
  25.         {
  26.             while (!isContentReady)
  27.             {
  28.                 Console.WriteLine("等待文件内容读取完成");
  29.                 Monitor.Wait(lockObject);
  30.             }
  31.             Console.WriteLine("开始解析文件内容: " + fileContent);
  32.         }
  33.     }
  34. }
  35. class Program
  36. {
  37.     static void Main()
  38.     {
  39.         FileProcessor processor = new FileProcessor();
  40.         new Thread(() => processor.ReadFile("test.txt")).Start();
  41.         new Thread(processor.ParseContent).Start();
  42.     }
  43. }
复制代码
在这个例子中,解析线程调用Monitor.Wait(lockObject)进入等待状态,当读取线程读取完文件内容后,调用Monitor.Pulse(lockObject)唤醒解析线程,解析线程从等待状态变为就绪状态,竞争锁资源,获取到锁后开始解析文件内容。
3.4 响应停止能力

Sleep()方法在执行期间,线程处于壅闭状态,它不会响应停止请求。如果在一个线程调用Sleep()期间,另一个线程对其发出停止请求,该线程会忽略这个停止信号,继续就寝直到指定的时间结束。比方,我们有一个定时任务线程,每隔一段时间执行一次任务,在任务执行过程中,不希望被停止干扰,代码如下:
  1. using System;
  2. using System.Threading;
  3. class ScheduledTask
  4. {
  5.     public void Run()
  6.     {
  7.         while (true)
  8.         {
  9.             Console.WriteLine("开始执行定时任务");
  10.             Thread.Sleep(5000);
  11.             Console.WriteLine("定时任务执行完成");
  12.         }
  13.     }
  14. }
  15. class Program
  16. {
  17.     static void Main()
  18.     {
  19.         Thread taskThread = new Thread(new ScheduledTask().Run);
  20.         taskThread.Start();
  21.         Thread.Sleep(3000);
  22.         taskThread.Interrupt();
  23.         Console.WriteLine("尝试中断定时任务线程");
  24.     }
  25. }
复制代码
在这个例子中,定时任务线程调用Thread.Sleep(5000)进入就寝状态,3 秒后主线程实验停止它,但定时任务线程会忽略停止请求,继续就寝直到 5 秒结束。
相比之下,Wait()方法可以响应停止请求。当一个线程在等待状态时,如果被其他线程停止,它会抛出InterruptedException非常,从而可以在捕获非常时举行相应的处置惩罚。比方,在一个多线程的网络通讯程序中,一个线程在等待服务器响应时,可能需要在某些情况下提前停止等待,代码如下:
  1. using System;
  2. using System.Threading;
  3. class NetworkClient
  4. {
  5.     private object lockObject = new object();
  6.     private bool isResponseReceived = false;
  7.     public void WaitForResponse()
  8.     {
  9.         lock (lockObject)
  10.         {
  11.             try
  12.             {
  13.                 while (!isResponseReceived)
  14.                 {
  15.                     Console.WriteLine("等待服务器响应");
  16.                     Monitor.Wait(lockObject);
  17.                 }
  18.                 Console.WriteLine("收到服务器响应");
  19.             }
  20.             catch (InterruptedException e)
  21.             {
  22.                 Console.WriteLine("等待被中断: " + e.Message);
  23.             }
  24.         }
  25.     }
  26.     public void SimulateResponse()
  27.     {
  28.         lock (lockObject)
  29.         {
  30.             isResponseReceived = true;
  31.             Console.WriteLine("模拟服务器响应,通知等待线程");
  32.             Monitor.Pulse(lockObject);
  33.         }
  34.     }
  35. }
  36. class Program
  37. {
  38.     static void Main()
  39.     {
  40.         NetworkClient client = new NetworkClient();
  41.         Thread waitThread = new Thread(client.WaitForResponse);
  42.         waitThread.Start();
  43.         Thread.Sleep(2000);
  44.         waitThread.Interrupt();
  45.         Console.WriteLine("中断等待线程");
  46.     }
  47. }
复制代码
在这个例子中,等待线程在调用Monitor.Wait(lockObject)进入等待状态后,2 秒后被主线程停止,会捕获InterruptedException非常并举行相应的处置惩罚。
四、适用场景

4.1 Sleep () 适用场景



  • 定时任务执行:在需要定时执行某项任务的场景中,Sleep()方法能发挥重要作用。比方,一个监控系统需要每隔一段时间(如 5 分钟)查抄一次服务器的状态,我们可以使用Sleep()方法来控制查抄的时间间隔。示例代码如下:
  1. using System;
  2. using System.Threading;
  3. class ServerMonitor
  4. {
  5.     public void StartMonitoring()
  6.     {
  7.         while (true)
  8.         {
  9.             Console.WriteLine("开始检查服务器状态");
  10.             // 模拟检查服务器状态的操作
  11.             // ......
  12.             Thread.Sleep(5 * 60 * 1000);
  13.         }
  14.     }
  15. }
  16. class Program
  17. {
  18.     static void Main()
  19.     {
  20.         ServerMonitor monitor = new ServerMonitor();
  21.         new Thread(monitor.StartMonitoring).Start();
  22.     }
  23. }
复制代码
在这段代码中,Thread.Sleep(5 * 60 * 1000)使得线程每 5 分钟执行一次服务器状态查抄操作。


  • 模拟耽误结果:在一些需要模拟耽误的场景,如网络请求耽误、动画结果耽误等,Sleep()方法可以简单地实现耽误功能。比如,在一个简单的登录验证功能中,为了模拟网络耽误,我们可以在验证逻辑执行后添加一个短暂的耽误,让用户感受到更真实的网络请求过程。示例代码如下:
  1. using System;
  2. using System.Threading;
  3. class LoginSystem
  4. {
  5.     public void Login(string username, string password)
  6.     {
  7.         Console.WriteLine("正在验证用户名和密码...");
  8.         // 模拟验证逻辑
  9.         bool isValid = ValidateCredentials(username, password);
  10.         if (isValid)
  11.         {
  12.             Thread.Sleep(2000);
  13.             Console.WriteLine("登录成功");
  14.         }
  15.         else
  16.         {
  17.             Thread.Sleep(2000);
  18.             Console.WriteLine("用户名或密码错误");
  19.         }
  20.     }
  21.     private bool ValidateCredentials(string username, string password)
  22.     {
  23.         // 简单的验证逻辑示例
  24.         return username == "admin" && password == "123456";
  25.     }
  26. }
  27. class Program
  28. {
  29.     static void Main()
  30.     {
  31.         LoginSystem loginSystem = new LoginSystem();
  32.         loginSystem.Login("admin", "123456");
  33.     }
  34. }
复制代码
在上述代码中,无论登录成功与否,都会通过Thread.Sleep(2000)模拟 2 秒的耽误,加强用户体验。
4.2 Wait () 适用场景



  • 生产者 - 消费者模子:这是Wait()方法最典型的应用场景。在该模子中,生产者线程负责生产数据并将其放入共享队列中,消费者线程则从队列中取出数据举行处置惩罚。当队列已满时,生产者线程需要等待;当队列已空时,消费者线程需要等待。通过Wait()和Notify()(或NotifyAll())方法,生产者和消费者线程可以实现高效的协作。比方,我们有一个生产包子和消费包子的场景,代码如下:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. class BaoziShop
  5. {
  6.     private Queue<string> baoziQueue = new Queue<string>();
  7.     private object lockObject = new object();
  8.     private const int MaxQueueSize = 10;
  9.     public void ProduceBaozi()
  10.     {
  11.         while (true)
  12.         {
  13.             lock (lockObject)
  14.             {
  15.                 while (baoziQueue.Count >= MaxQueueSize)
  16.                 {
  17.                     Console.WriteLine("包子队列已满,生产者等待");
  18.                     Monitor.Wait(lockObject);
  19.                 }
  20.                 string baozi = "新生产的包子";
  21.                 baoziQueue.Enqueue(baozi);
  22.                 Console.WriteLine("生产了一个包子,当前队列中有 " + baoziQueue.Count + " 个包子");
  23.                 Monitor.PulseAll(lockObject);
  24.             }
  25.         }
  26.     }
  27.     public void ConsumeBaozi()
  28.     {
  29.         while (true)
  30.         {
  31.             lock (lockObject)
  32.             {
  33.                 while (baoziQueue.Count == 0)
  34.                 {
  35.                     Console.WriteLine("包子队列已空,消费者等待");
  36.                     Monitor.Wait(lockObject);
  37.                 }
  38.                 string baozi = baoziQueue.Dequeue();
  39.                 Console.WriteLine("消费了一个包子,当前队列中有 " + baoziQueue.Count + " 个包子");
  40.                 Monitor.PulseAll(lockObject);
  41.             }
  42.         }
  43.     }
  44. }
  45. class Program
  46. {
  47.     static void Main()
  48.     {
  49.         BaoziShop shop = new BaoziShop();
  50.         new Thread(shop.ProduceBaozi).Start();
  51.         new Thread(shop.ConsumeBaozi).Start();
  52.     }
  53. }
复制代码
在这个例子中,当包子队列满时,生产者线程调用Monitor.Wait(lockObject)进入等待状态并释放锁;当包子队列空时,消费者线程调用Monitor.Wait(lockObject)进入等待状态并释放锁。当生产者生产了包子大概消费者消费了包子后,会调用Monitor.PulseAll(lockObject)通知等待的线程。


  • 线程间资源竞争与协作:在多个线程需要访问共享资源,并且需要根据资源的状态举行协作的场景中,Wait()方法能很好地和谐线程间的执行次序。比方,在一个多线程的文件读取和处置惩罚系统中,有一个线程负责读取文件内容,另一个线程负责对读取的内容举行解析。当读取线程还未完成读取时,解析线程需要等待;当读取线程完成读取后,通知解析线程开始工作。示例代码如下:
  1. using System;
  2. using System.IO;
  3. using System.Threading;
  4. class FileHandler
  5. {
  6.     private object lockObject = new object();
  7.     private string fileContent;
  8.     private bool isContentReady = false;
  9.     public void ReadFile(string filePath)
  10.     {
  11.         lock (lockObject)
  12.         {
  13.             using (StreamReader reader = new StreamReader(filePath))
  14.             {
  15.                 fileContent = reader.ReadToEnd();
  16.             }
  17.             isContentReady = true;
  18.             Console.WriteLine("文件读取完成,通知解析线程");
  19.             Monitor.Pulse(lockObject);
  20.         }
  21.     }
  22.     public void ParseContent()
  23.     {
  24.         lock (lockObject)
  25.         {
  26.             while (!isContentReady)
  27.             {
  28.                 Console.WriteLine("等待文件内容读取完成");
  29.                 Monitor.Wait(lockObject);
  30.             }
  31.             Console.WriteLine("开始解析文件内容: " + fileContent);
  32.         }
  33.     }
  34. }
  35. class Program
  36. {
  37.     static void Main()
  38.     {
  39.         FileHandler handler = new FileHandler();
  40.         new Thread(() => handler.ReadFile("test.txt")).Start();
  41.         new Thread(handler.ParseContent).Start();
  42.     }
  43. }
复制代码
在这段代码中,解析线程调用Monitor.Wait(lockObject)进入等待状态,直到读取线程完成文件读取并调用Monitor.Pulse(lockObject)通知它。
五、注意事项

5.1 Sleep () 使用注意

在使用Sleep()方法时,需要注意制止因设置过长的就寝时间导致程序假死或响应迟缓。比方,在一个图形用户界面(GUI)应用程序中,如果主线程调用了Sleep()方法且就寝时间较长,那么界面将无法响应用户的操作,如点击按钮、拖动窗口等,严峻影响用户体验 。
  1. using System;
  2. using System.Threading;
  3. using System.Windows.Forms;
  4. namespace SleepInGUI
  5. {
  6.     public partial class Form1 : Form
  7.     {
  8.         public Form1()
  9.         {
  10.             InitializeComponent();
  11.         }
  12.         private void button1_Click(object sender, EventArgs e)
  13.         {
  14.             // 错误示范:在主线程中长时间Sleep
  15.             Thread.Sleep(10000);
  16.             MessageBox.Show("睡眠结束");
  17.         }
  18.     }
  19. }
复制代码
在上述代码中,当用户点击按钮时,主线程会进入 10 秒的就寝状态,在此期间,整个应用程序的界面将处于无响应状态。为了制止这种情况,可以考虑将耗时的操作放在新的线程中执行,而不是在主线程中调用Sleep()。
5.2 Wait () 使用注意



  • 锁的正确管理:使用Wait()方法时,必须确保正确地获取和释放锁。如果在调用Wait()之前没有获取锁,大概在不适当的地方释放锁,都可能导致程序出现不可预测的结果,甚至死锁。比方:
  1. using System;
  2. using System.Threading;
  3. class DeadlockExample
  4. {
  5.     private static object lockObject1 = new object();
  6.     private static object lockObject2 = new object();
  7.     public static void Thread1()
  8.     {
  9.         lock (lockObject1)
  10.         {
  11.             Console.WriteLine("Thread1 获取了 lockObject1");
  12.             // 错误示范:在未获取lockObject2的情况下尝试等待
  13.             Monitor.Wait(lockObject2);
  14.         }
  15.     }
  16.     public static void Thread2()
  17.     {
  18.         lock (lockObject2)
  19.         {
  20.             Console.WriteLine("Thread2 获取了 lockObject2");
  21.             // 错误示范:在未获取lockObject1的情况下尝试等待
  22.             Monitor.Wait(lockObject1);
  23.         }
  24.     }
  25. }
  26. class Program
  27. {
  28.     static void Main()
  29.     {
  30.         new Thread(DeadlockExample.Thread1).Start();
  31.         new Thread(DeadlockExample.Thread2).Start();
  32.     }
  33. }
复制代码
在这段代码中,Thread1和Thread2分别获取了不同的锁,然后实验在未获取的锁上调用Wait(),这将导致死锁。正确的做法是确保在调用Wait()之前,线程已经获取了对应的锁。


  • 制止死锁:除了正确管理锁之外,还需要注意制止因线程间的循环等待而导致死锁。在复杂的多线程场景中,多个线程可能会相互依靠,形成循环等待的情况。比方,假设有三个线程A、B、C,它们分别持有锁lockA、lockB、lockC,并且A等待B释放lockB,B等待C释放lockC,C等待A释放lockA,这样就会形成死锁。为了制止这种情况,需要仔细设计线程的同步逻辑,确保不会出现循环等待的情况。
  • 处置惩罚线程唤醒后的竞争问题:当一个线程被Notify()或NotifyAll()唤醒后,它会与其他线程竞争锁资源。在高并发的情况下,可能会出现竞争激烈的情况,导致性能降落。为了缓解这种情况,可以考虑使用一些并发控制的本领,如信号量(Semaphore)、互斥锁(Mutex)等,来控制线程的访问次序和并发度。比方,使用信号量来限制同时访问某个资源的线程数量:
  1. using System;
  2. using System.Threading;
  3. class ResourceAccess
  4. {
  5.     private static Semaphore semaphore = new Semaphore(2, 2);
  6.     private static object lockObject = new object();
  7.     public static void AccessResource()
  8.     {
  9.         semaphore.WaitOne();
  10.         lock (lockObject)
  11.         {
  12.             try
  13.             {
  14.                 Console.WriteLine("线程进入资源访问区");
  15.                 Thread.Sleep(2000);
  16.                 Console.WriteLine("线程完成资源访问");
  17.             }
  18.             finally
  19.             {
  20.                 semaphore.Release();
  21.             }
  22.         }
  23.     }
  24. }
  25. class Program
  26. {
  27.     static void Main()
  28.     {
  29.         for (int i = 0; i < 5; i++)
  30.         {
  31.             new Thread(ResourceAccess.AccessResource).Start();
  32.         }
  33.     }
  34. }
复制代码
在这个例子中,Semaphore被初始化为允许最多 2 个线程同时访问资源,通过WaitOne()和Release()方法来控制线程的进入和离开,从而制止了过多线程同时竞争资源的情况。
六、总结

在 C# 多线程编程领域,Sleep()和Wait()作为控制线程执行流程的重要本领,各自有着独特的功能和适用场景。Sleep()简单直接,适用于需要线程停息固定时长的场景,像定时任务、模拟耽误结果等,其不会释放锁的特性,在特定业务逻辑中能保证数据的一致性和操作的原子性。而Wait()则更偏重于线程间的协作与同步,通过释放锁,让多个线程能高效地访问共享资源,在生产者 - 消费者模子以及线程间资源竞争与协作场景中发挥着关键作用。
理解这两个方法的焦点区别,并根据实际需求合理运用,是编写高效、稳定多线程程序的关键。希望通过本文的探讨,能帮助大家在多线程编程的道路上更加得心应手,编写出更健壮、更具扩展性的代码。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

三尺非寒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表