面试常考:C#用两个线程交替打印1-100的五种方法

  • 面试常考:C#用两个线程交替打印1-100的五种方法已关闭评论
  • 162 次浏览
  • A+
所属分类:.NET技术
摘要

    “C#用两个线程交替打印1-100的五种方法”是.NET工程师面试多线程常考的试题之一,主要考察对C#语法和对多线程的熟悉程度。本文将用5种方法实现这个面试题。

    "C#用两个线程交替打印1-100的五种方法"是.NET工程师面试多线程常考的试题之一,主要考察对C#语法和对多线程的熟悉程度。本文将用5种方法实现这个面试题。

方法1:使用Mutex或lock

    这种方法涉及使用Mutex或lock对象来同步两个线程。其中一个线程负责打印偶数,另一个线程负责打印奇数。线程在执行任务之前会锁定共享的Mutex或lock对象,以确保每个线程执行任务时只有一个线程能够访问共享资源。代码如下:

class Program {     static Mutex mutex = new Mutex();     static int count = 1;     static void Main(string[] args)     {         Thread t1 = new Thread(PrintOddNumbers);         Thread t2 = new Thread(PrintEvenNumbers);         t1.Start();         t2.Start();         t1.Join();         t2.Join();         Console.ReadLine();     }     static void PrintOddNumbers()     {         while (count <= 100)         {             mutex.WaitOne();             if (count % 2 == 1)             {                 Console.WriteLine("Thread 1: " + count);                 count++;             }             mutex.ReleaseMutex();         }     }     static void PrintEvenNumbers()     {         while (count <= 100)         {             mutex.WaitOne();             if (count % 2 == 0)             {                 Console.WriteLine("Thread 2: " + count);                 count++;             }             mutex.ReleaseMutex();         }     } }

方法2:使用AutoResetEvent

AutoResetEvent是一种线程同步机制,允许一个线程等待另一个线程发出信号来继续执行。其中一个线程负责打印奇数,另一个线程负责打印偶数。当一个线程完成打印任务时,它发出信号以唤醒另一个线程来继续执行。

class Program {     static AutoResetEvent oddEvent = new AutoResetEvent(false);     static AutoResetEvent evenEvent = new AutoResetEvent(false);     static int count = 1;     static void Main(string[] args)     {         Thread t1 = new Thread(PrintOddNumbers);         Thread t2 = new Thread(PrintEvenNumbers);         t1.Start();         t2.Start();         t1.Join();         t2.Join();         Console.ReadLine();     }      static void PrintOddNumbers()     {         while (count <= 100)         {             if (count % 2 == 1)             {                 Console.WriteLine("Thread 1: " + count);                 count++;                 evenEvent.Set();                 oddEvent.WaitOne();             }         }     }      static void PrintEvenNumbers()     {         while (count <= 100)         {             if (count % 2 == 0)             {                 Console.WriteLine("Thread 2: " + count);                 count++;                 oddEvent.Set();                 evenEvent.WaitOne();             }         }     } //欢迎关注公众号“DOTNET开发跳槽”,关注可获得海量面试题

方法3:使用Monitor

Monitor是C#中的一种同步机制,类似于Mutex。其中一个线程负责打印奇数,另一个线程负责打印偶数。线程在执行任务之前会锁定共享的Monitor对象,以确保每个线程执行任务时只有一个线程能够访问共享资源。

class Program {     static object lockObj = new object();     static int count = 1;     static void Main(string[] args)     {         Thread t1 = new Thread(PrintOddNumbers);         Thread t2 = new Thread(PrintEvenNumbers);         t1.Start();         t2.Start();         t1.Join();         t2.Join();         Console.ReadLine();     }      static void PrintOddNumbers()     {         while (count <= 100)         {             lock (lockObj)             {                 if (count % 2 == 1)                 {                     Console.WriteLine("Thread 1: " + count);                     count++;                 }             }         }     }      static void PrintEvenNumbers()     {         while (count <= 100)         {             lock (lockObj)             {                 if (count % 2 == 0)                 {                     Console.WriteLine("Thread 2: " + count);                     count++;                 }             }         }     } }

方法4:使用信号量Semaphore

Semaphore是一种同步机制,允许多个线程同时访问共享资源。其中一个线程负责打印奇数,另一个线程负责打印偶数。线程在执行任务之前会等待信号量,以确保每个线程只有在获得信号量之后才能访问共享资源。

class Program {     static Semaphore semaphore = new Semaphore(1, 1);     static int count = 1;     static void Main(string[] args)     {         Thread t1 = new Thread(PrintOddNumbers);         Thread t2 = new Thread(PrintEvenNumbers);         t1.Start();         t2.Start();         t1.Join();         t2.Join();          Console.ReadLine();     }      static void PrintOddNumbers()     { //注意 这里是99,否则会出现101         while (count <= 99)         {             semaphore.WaitOne();             if (count % 2 == 1)             {                 Console.WriteLine("Thread 1: " + count);                 count++;             }             semaphore.Release();         }     }      static void PrintEvenNumbers()     {         while (count <= 100)         {             semaphore.WaitOne();             if (count % 2 == 0)             {                 Console.WriteLine("Thread 2: " + count);                 count++;             }             semaphore.Release();         }     } }

方法5:使用Task和async/await

在C#中,使用Task和async/await关键字可以轻松地在两个线程之间切换执行。其中一个线程负责打印奇数,另一个线程负责打印偶数。线程在执行任务之前使用async/await等待异步任务完成,以确保每个线程只在异步任务完成后才访问共享资源。

class Program {     static int count = 1;     static void Main(string[] args)     {         Task.Run(PrintOddNumbers);       // 这里改成这个也可以       // var thread1 = new Thread(PrintOddNumbers);         Task.Run(PrintEvenNumbers);         Console.ReadLine();     }   //如果用Thread改成同步方法     static async Task PrintOddNumbers()     {         while (count <= 100)         {             if (count % 2 == 1)             {                 Console.WriteLine("Thread 1: " + count);                 count++;                 //如果用Thread这里改成 Thread.Sleep(1);                 await Task.Delay(1);             }         }     }     static async Task PrintEvenNumbers()     {         while (count <= 100)         {             if (count % 2 == 0)             {                 Console.WriteLine("Thread 2: " + count);                 count++;                 await Task.Delay(1);             }         }     } //欢迎关注公众号“DOTNET开发跳槽”,关注可获得海量面试题

五种效果如下:

面试常考:C#用两个线程交替打印1-100的五种方法

 

 

以上五种方法各有优缺点,没有一种方法是绝对最优的,取决于应用场景和要求。以下是对五种方法的简单比较和说明:1、使用ManualResetEventWaitHandle:这种方法在实现上较为简单,但是由于线程必须互斥地访问共享资源,因此会导致性能瓶颈。此外,使用ManualResetEventWaitHandle需要频繁调用WaitOne和Set方法,可能会降低应用程序的响应能力。2、使用AutoResetEventWaitHandle:这种方法在实现上比较简单,而且使用AutoResetEventWaitHandle可以避免性能瓶颈问题。然而,它仍然需要频繁调用WaitOne和Set方法,可能会降低应用程序的响应能力。3、使用锁:使用锁可以避免性能瓶颈问题,因为同一时间只有一个线程可以访问共享资源。但是,锁可能会导致线程死锁和性能下降的问题,因此需要小心使用。4、使用信号量Semaphore:这种方法可以避免性能瓶颈问题,并允许多个线程同时访问共享资源。Semaphore还可以设置多个许可证,以控制并发线程的数量。然而,使用Semaphore可能会使代码变得更加复杂,因此需要小心使用。5、使用Task和async/await:这种方法可以避免性能瓶颈问题,并且使用Task和async/await可以使代码更加简洁易懂。但是,它可能会对内存和CPU产生额外的开销,因为需要在任务之间频繁地切换上下文。综上所述,选择哪种方法取决于应用程序的要求和程序员的个人偏好。如果应用程序需要更好的性能,则应该使用锁或信号量;如果应用程序需要更简洁易懂的代码,则应该使用Task和async/await。

考察的知识点

1、C#编程语言和语法:实现多线程程序需要熟悉C#编程语言和语法,包括线程的创建和管理、共享资源的访问和同步等方面的知识。2、多线程编程:多线程编程是指同时运行多个线程的编程模型,它可以提高应用程序的性能和响应能力。多线程编程需要考虑线程的同步、共享资源的访问、线程间的通信等问题。3、线程同步机制:线程同步机制是指用于控制多个线程访问共享资源的机制,常用的线程同步机制包括锁、信号量、事件等。4、异步编程:异步编程是指不阻塞线程并且在完成任务后通知线程的编程模型,它可以提高应用程序的响应能力和性能。异步编程需要熟悉异步和await关键字、Task和Task<T>等类型、async和await方法等概念。参考:chatGPT