Advertisement
Guest User

Untitled

a guest
Sep 21st, 2019
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 11.06 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6.  
  7. namespace threads
  8. {
  9.     class Program
  10.     {
  11.         const int threadCount = 8;
  12.         static Random random = new Random();
  13.         static void Main(string[] args)
  14.         {
  15.             // Вначале без всяких выдумок заполняем список контроллеров потоков.
  16.             var pool = new List<ThreadController>();
  17.             for (int i = 0; i < threadCount; i++)
  18.             {
  19.                 var controller = new ThreadController();
  20.                 // По ходу создаём потоки и стартуем их присвоив имя.
  21.                 var thread = controller.CreateThread(start: false);
  22.                 thread.Name = $"Thread {i + 1}";
  23.                 thread.Start();
  24.                 pool.Add(controller);
  25.             }
  26.             Console.WriteLine("Commands: Stop, Counter, stopWatch");
  27.             while (true)
  28.             {
  29.                 // Перед следующей командой ждём когда все потоки завершат свои задания.
  30.                 pool.WaitAll();
  31.                 var info = $"[{pool.Count, 3} threads]";
  32.                 Console.Write(info + "> ");
  33.                 var input = Console.ReadLine();
  34.                 if (input == "s") // Стопаем все потоки по запросу.
  35.                 {
  36.                     pool.StopWait();
  37.                 }
  38.                 else if (input == "c") // Заставляет все потоки инкрементировать один счётчик.
  39.                                        //Странно, но я ни разу не получал ошибку этого счётчика.
  40.                 {
  41.                     var counter = (value: (double)0, false);
  42.                     pool.SetJob(() => Console.WriteLine($"{counter.value++} - {Thread.CurrentThread.Name}")).WaitAll();
  43.                     Console.WriteLine($"Result is {counter.value}");
  44.                 }
  45.                 else if (input == "w") // Рандомное ожидание и общее время, прошедшее в главном потоке.
  46.                 {
  47.                     var t = new System.Diagnostics.Stopwatch();
  48.                     t.Start();
  49.                     pool.SetJob(RandomWait).WaitAll();
  50.                     t.Stop();
  51.                     Console.WriteLine($"Total: {t.ElapsedMilliseconds}");
  52.                 }
  53.                 else if (input == "t") // Как предыдущее, только показывает прикольную цепочку назначения заданий.
  54.                 {
  55.                     var t = new System.Diagnostics.Stopwatch();
  56.                     t.Start();
  57.                     pool.SetJob(RandomWait).SetJob(RandomWait).SetJob(RandomWait).WaitAll();
  58.                     t.Stop();
  59.                     Console.WriteLine($"Total: {t.ElapsedMilliseconds}");
  60.                 }
  61.                 else // Тупо напечатать ввод пользователя.
  62.                 {
  63.                     pool.SetJob(() => { Console.WriteLine($"{input} - {Thread.CurrentThread.Name}"); });
  64.                 }
  65.             }
  66.         }
  67.         /// <summary>
  68.         /// Приостанавливает исполнение потока на случайное время до 1 секунды.
  69.         /// </summary>
  70.         static void RandomWait()
  71.         {
  72.             var amount = random.Next(30, 1000);
  73.             Thread.Sleep(amount);
  74.             Console.WriteLine($"{Thread.CurrentThread.Name, -10} slept for {amount, 4} milliseconds.");
  75.         }
  76.     }
  77.  
  78.     /// <summary>
  79.     /// Описывает возможные состояния потока/контроллера потока
  80.     /// </summary>
  81.     enum ThreadWorkState : byte
  82.     {
  83.         /// <summary>
  84.         /// Поток ожидает задачи
  85.         /// </summary>
  86.         Waiting,
  87.         /// <summary>
  88.         /// Поток обрабатывает задачу
  89.         /// </summary>
  90.         Working,
  91.         /// <summary>
  92.         /// Поток останавливается
  93.         /// </summary>
  94.         Terminating
  95.     }
  96.  
  97.     /// <summary>
  98.     /// Управляет исполнением потока. Определяет точку входа для новых потоков, в которой ожидает заданий.
  99.     /// </summary>
  100.     class ThreadController
  101.     {
  102.         private Thread associatedThread;
  103.         private Action job;
  104.         /// <summary>
  105.         /// Текущее состояние потока
  106.         /// </summary>
  107.         public ThreadWorkState State { get; set; }
  108.         /// <summary>
  109.         /// Задача, исполняемая потоком. <code>null</code> если текущей задачи нет.
  110.         /// </summary>
  111.         public Action Job { get => job; set => SetJob(value); }
  112.         /// <summary>
  113.         /// Имя контролируемого потока.
  114.         /// </summary>
  115.         public string ThreadName => Thread.CurrentThread.Name ?? "Unnamed thread";
  116.         public ThreadController()
  117.         {
  118.             State = ThreadWorkState.Waiting;
  119.         }
  120.         /// <summary>
  121.         /// Точка входа для нового потока. Назначается при создании экземпляра Thread.
  122.         /// Не следует вызывать этот метод напрямую из главного потока.
  123.         /// </summary>
  124.         public void Main()
  125.         {
  126.             Console.WriteLine($"{ThreadName} spawned.");
  127.             while (true)
  128.             {
  129.                 SpinWait();
  130.                 if (State == ThreadWorkState.Working && Job != null)
  131.                 {
  132.                     Job();
  133.                     job = null;
  134.                     State = ThreadWorkState.Waiting;
  135.                 }
  136.                 else if (State == ThreadWorkState.Terminating)
  137.                 {
  138.                     break;
  139.                 }
  140.             }
  141.         }
  142.         void SpinWait()
  143.         {
  144.             Thread.SpinWait(2 << 10);
  145.         }
  146.         /// <summary>
  147.         /// Выводит поток из цикла ожидания задачи. Если задачи нет, поток останавливается и закрывается.
  148.         /// </summary>
  149.         public void StopWait()
  150.         {
  151.             State = ThreadWorkState.Terminating;
  152.             Console.WriteLine($"{ThreadName} terminating.");
  153.         }
  154.         private void SetJob(Action job)
  155.         {
  156.             if (State == ThreadWorkState.Waiting)
  157.             {
  158.                 this.job = job;
  159.                 State = ThreadWorkState.Working;
  160.             }
  161.             else
  162.             {
  163.                 throw new InvalidOperationException("Поток не находился в ожидании задачи.");
  164.             }
  165.         }
  166.         /// <summary>
  167.         /// Создаёт новый поток с Main в качестве точки входа.
  168.         /// </summary>
  169.         /// <param name="start">Показывает, нужно ли начать исполнение потока сразу при создании</param>
  170.         /// <returns>Возвращает новый поток, управляемый текущим экземпляром ThreadController</returns>
  171.         public Thread CreateThread(bool start = true)
  172.         {
  173.             if (associatedThread != null)
  174.                 throw new InvalidOperationException("Этот контроллер уже явно связан с потоком");
  175.             var thread = new Thread(Main);
  176.             if (start)
  177.                 thread.Start();
  178.             associatedThread = thread;
  179.             return thread;
  180.         }
  181.     }
  182.    
  183.     /// <summary>
  184.     /// Определяет набор методов расширений для коллекций ThreadController, упрощающих управление.
  185.     /// </summary>
  186.     static class ThreadControllersExtensions
  187.     {
  188.         /// <summary>
  189.         /// В цикле ожидает, когда все потоки перейдут в состояние ожидания следующей задачи.
  190.         /// </summary>
  191.         /// <param name="pool"></param>
  192.         public static ICollection<ThreadController> WaitAll(this ICollection<ThreadController> pool)
  193.         {
  194.             while (true)
  195.             {
  196.                 if (pool.All(c => c.State == ThreadWorkState.Waiting))
  197.                 {
  198.                     break;
  199.                 }
  200.                 Thread.SpinWait(2 << 10);
  201.             }
  202.             return pool;
  203.         }
  204.         /// <summary>
  205.         /// Устанавливает задачу <paramref name="job"/> для всех потоков из пула.
  206.         /// Если существуют потоки, у которых уже есть задача - сначала завершится выполнение этой задачи.
  207.         /// </summary>
  208.         public static ICollection<ThreadController> SetJob(this ICollection<ThreadController> pool, Action job)
  209.         {
  210.             var flaggedPool = pool.WithFlags();
  211.             while (flaggedPool.Any(x => !x.flag))
  212.             {
  213.                 foreach (var item in flaggedPool.Where(x => !x.flag && x.controller.State == ThreadWorkState.Waiting))
  214.                 {
  215.                     item.controller.Job = job;
  216.                     item.flag = true;
  217.                 }
  218.                 Thread.SpinWait(2 << 10);
  219.             }
  220.             return pool;
  221.         }
  222.         /// <summary>
  223.         /// Выводит все потоки из цикла ожидания работы и удаляет все контроллеры потоков из пула, оставляя пустую коллекцию.
  224.         /// </summary>
  225.         public static void StopWait(this ICollection<ThreadController> pool)
  226.         {
  227.             foreach (var controller in pool)
  228.             {
  229.                 controller.StopWait();
  230.             }
  231.             while (pool.Count > 0)
  232.             {
  233.                 pool.Remove(pool.First());
  234.             }
  235.         }
  236.         static ICollection<ControllerWithFlag> WithFlags(this ICollection<ThreadController> pool) => pool.Select(x => new ControllerWithFlag(x)).ToList();
  237.  
  238.         /// <summary>
  239.         /// Маленький служебный класс, нужен чтобы не засорять ThreadController мутабельным флагом.
  240.         /// </summary>
  241.         class ControllerWithFlag
  242.         {
  243.             internal ThreadController controller;
  244.             internal bool flag;
  245.             internal ControllerWithFlag(ThreadController controller)
  246.             {
  247.                 this.controller = controller;
  248.                 flag = false;
  249.             }
  250.             public static implicit operator ThreadController(ControllerWithFlag value)
  251.             {
  252.                 return value.controller;
  253.             }
  254.         }
  255.     }
  256. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement