"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,否则会出现101while (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开发跳槽”,关注可获得海量面试题
五种效果如下:
以上五种方法各有优缺点,没有一种方法是绝对最优的,取决于应用场景和要求。以下是对五种方法的简单比较和说明:
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