Prueba de entrevista frecuente: C# usa dos subprocesos para imprimir alternativamente 1-100 cinco métodos

"Cinco formas de imprimir alternativamente 1-100 usando dos subprocesos en C#" es una de las preguntas más frecuentes en las entrevistas de subprocesos múltiples para ingenieros de .NET. Examina principalmente la familiaridad con la sintaxis de C# y los subprocesos múltiples. Este artículo implementará esta pregunta de la entrevista de 5 maneras.

Método 1: usar Mutex o bloqueo

    Este enfoque implica el uso de un objeto Mutex o de bloqueo para sincronizar dos subprocesos. Uno de los hilos es responsable de imprimir números pares y el otro hilo es responsable de imprimir números impares. El subproceso bloqueará el Mutex compartido o bloqueará el objeto antes de ejecutar la tarea para garantizar que solo un subproceso pueda acceder al recurso compartido cuando cada subproceso ejecute la tarea. el código se muestra a continuación:

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();
        }
    }
}

 

Método 2: Usar AutoResetEvent

AutoResetEvent es un mecanismo de sincronización de subprocesos que permite que un subproceso espere a que otro subproceso indique que continúa la ejecución. Uno de los subprocesos es responsable de imprimir números impares y el otro subproceso es responsable de imprimir números pares. Cuando un subproceso termina de imprimir, indica que se despierte otro subproceso para continuar la ejecución.

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();
            }
        }
    }

 

Método 3: usar el monitor

Monitor es un mecanismo de sincronización en C#, similar a Mutex. Uno de los subprocesos es responsable de imprimir números impares y el otro subproceso es responsable de imprimir números pares. Los subprocesos bloquearán el objeto Monitor compartido antes de ejecutar tareas para garantizar que solo un subproceso pueda acceder a los recursos compartidos cuando cada subproceso ejecuta tareas.

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++;
                }
            }
        }
    }
}

 

Método 4: usar semáforo semáforo

Semaphore es un mecanismo de sincronización que permite que múltiples subprocesos accedan a recursos compartidos al mismo tiempo. Uno de los subprocesos es responsable de imprimir números impares y el otro subproceso es responsable de imprimir números pares. Los subprocesos esperan en el semáforo antes de ejecutar tareas para garantizar que cada subproceso pueda acceder a los recursos compartidos solo después de adquirir el semáforo.

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();
        }
    }
}

 

Método 5: Usar Task y async/await

En C#, es fácil cambiar la ejecución entre dos subprocesos mediante las palabras clave Task y async/await. Uno de los subprocesos es responsable de imprimir números impares y el otro subproceso es responsable de imprimir números pares. Los subprocesos usan async/await para esperar la finalización de la tarea asincrónica antes de ejecutar la tarea para garantizar que cada subproceso solo acceda al recurso compartido después de la finalización de la tarea asincrónica.

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);
            }
        }
    }

 Los cinco efectos son los siguientes:

 

 Los cinco métodos anteriores tienen sus propias ventajas y desventajas, y ninguno de ellos es absolutamente óptimo, según los escenarios y requisitos de la aplicación. La siguiente es una comparación simple y una descripción de los cinco métodos: 1. Use ManualResetEventWaitHandle: este método es relativamente simple en la implementación, pero debido a que los subprocesos deben tener acceso mutuamente exclusivo a los recursos compartidos, causará cuellos de botella en el rendimiento. Además, el uso de ManualResetEventWaitHandle requiere llamadas frecuentes a los métodos WaitOne y Set, lo que puede reducir la capacidad de respuesta de la aplicación. 2. Use AutoResetEventWaitHandle: este método es relativamente simple en la implementación y el uso de AutoResetEventWaitHandle puede evitar cuellos de botella en el rendimiento. Sin embargo, aún requiere llamadas frecuentes a los métodos WaitOne y Set, lo que posiblemente reduzca la capacidad de respuesta de la aplicación. 3. Use bloqueos: el uso de bloqueos puede evitar cuellos de botella en el rendimiento, ya que solo un subproceso puede acceder a los recursos compartidos al mismo tiempo. Sin embargo, los bloqueos pueden causar interbloqueos de subprocesos y problemas de rendimiento, por lo que deben usarse con cuidado. 4. Usar semáforo Semáforo: este método puede evitar cuellos de botella en el rendimiento y permitir que varios subprocesos accedan a los recursos compartidos al mismo tiempo. Semaphore también puede configurar varias licencias para controlar la cantidad de subprocesos simultáneos. Sin embargo, el uso de Semaphore puede hacer que el código sea más complicado, por lo que debe usarse con cuidado. 5. Use Task y async/await: este método puede evitar cuellos de botella en el rendimiento, y usar Task y async/await puede hacer que el código sea más conciso y fácil de entender. Sin embargo, puede incurrir en una sobrecarga adicional de memoria y CPU debido a la necesidad de cambiar contextos entre tareas con frecuencia. En resumen, qué método elegir depende de los requisitos de la aplicación y la preferencia personal del programador. Si su aplicación necesita un mejor rendimiento, debe usar bloqueos o semáforos; si su aplicación necesita un código más conciso y comprensible, debe usar Task y async/await.

Puntos de conocimiento de inspección

1. Lenguaje y sintaxis de programación C#: para implementar programas de subprocesos múltiples, debe estar familiarizado con el lenguaje y la sintaxis de programación C#, incluido el conocimiento de la creación y administración de subprocesos, el acceso y la sincronización de recursos compartidos, etc. 2. Programación de subprocesos múltiples: la programación de subprocesos múltiples se refiere al modelo de programación de ejecutar varios subprocesos al mismo tiempo, lo que puede mejorar el rendimiento y la capacidad de respuesta de la aplicación. La programación de subprocesos múltiples debe tener en cuenta cuestiones como la sincronización de subprocesos, el acceso a recursos compartidos y la comunicación entre subprocesos. 3. Mecanismo de sincronización de subprocesos: el mecanismo de sincronización de subprocesos se refiere al mecanismo utilizado para controlar múltiples subprocesos para acceder a recursos compartidos. Los mecanismos de sincronización de subprocesos comúnmente utilizados incluyen bloqueos, semáforos, eventos, etc. 4. Programación asincrónica: la programación asincrónica se refiere a un modelo de programación que no bloquea los subprocesos y notifica los subprocesos después de completar las tareas, lo que puede mejorar la capacidad de respuesta y el rendimiento de las aplicaciones. La programación asincrónica requiere familiaridad con las palabras clave asincrónicas y de espera, tipos como Task y Task<T>, y conceptos como los métodos async y await.

Supongo que te gusta

Origin blog.csdn.net/lwf3115841/article/details/130821223
Recomendado
Clasificación