Информация об изменениях

Сообщение Красивое решение для задачи с потоками от 12.03.2015 21:06

Изменено 12.03.2015 21:52 Cynic

Требуется найти "красивое" решение для следующей задачи.
Есть четыре потока: один "управляющий", и три "вычислительных". Задача "управляющего потока" раздавать задачи "вычислительным потокам", а задача "вычислительных" выполнять эти задачи. При этом "вычислительные потоки" должны работать по следующему алгоритму:
  • Стоит на паузе в ожидании задачи
  • Получает очередную задачу
  • Получает команду на старт
  • Выполняет вычисления
  • Становится на паузу в ожидании следующей задачи (возврат к первому пункту списка)
Кроме того:
  • Все три потока должны начинать работу "одновременно" (это ограничение само появляется из следующего требования)
  • Каждый поток должен дожидаться пока остальные два потока не выполнят работу
  • Управляющий поток должен дожидаться пока все вычислительные потоки не встанут на паузу и только после этого раздавать им задачи
  • Каждый поток должен быть создан только один раз
Пытаясь решить эту задачу я написал следующий тестовый пример:
    class Program
    {
        static AutoResetEvent mt = new AutoResetEvent(false);
        static AutoResetEvent th1 = new AutoResetEvent(false);
        static AutoResetEvent th2 = new AutoResetEvent(false);
        static AutoResetEvent th3 = new AutoResetEvent(false);

        static Thread mainThread;
        static Thread thread1;
        static Thread thread2;
        static Thread thread3;

        static void Main(string[] args)
        {
            mainThread = new Thread(MainThread);
            mainThread.IsBackground = true;

            thread1 = new Thread(Thread1);
            thread1.IsBackground = true;

            thread2 = new Thread(Thread2);
            thread2.IsBackground = true;

            thread3 = new Thread(Thread3);
            thread3.IsBackground = true;

            thread1.Start();
            thread2.Start();
            thread3.Start();
            mainThread.Start();

            Console.ReadLine();
        }

        static void MainThread()
        {
            while (true)
            {
                Console.Write("\nT ");

                if ((thread1.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread2.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread3.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin)
                {
                    mt.WaitOne();
                }

                if ((thread1.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread2.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread3.ThreadState & ThreadState.Running) == ThreadState.Running)
                {
                    th1.Set();
                    th2.Set();
                    th3.Set();
                }

                Thread.Sleep(1000);
            }
        }

        static void Thread1()
        {
            while (true)
            {
                mt.Set();
                th1.WaitOne();
                Console.Write("1");
            }
        }

        static void Thread2()
        {
            while (true)
            {
                mt.Set();
                th2.WaitOne();
                Console.Write("2");
            }
        }

        static void Thread3()
        {
            while (true)
            {
                mt.Set();
                th3.WaitOne();
                Console.Write("3");
            }
        }
    }

И всё вроде работает, но есть две вещи которые меня не устраивают:
  • Наличие большого количества экземпляров AutoResetEvent
  • Необходимость выполнять проверку состояния потока через ThreadState
Не устраивают они меня потому, что пока нужно только три "вычислительных" потока с этим можно жить, а представьте во что это превратиться если их будет пару десятков.
Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?
Красивое решение для задачи с потоками
Требуется найти "красивое" решение для следующей задачи.
Есть четыре потока: один "управляющий", и три "вычислительных". Задача "управляющего потока" раздавать задачи "вычислительным потокам", а задача "вычислительных" выполнять эти задачи. При этом "вычислительные потоки" должны работать по следующему алгоритму:
  • Стоит на паузе в ожидании задачи
  • Получает очередную задачу
  • Получает команду на старт
  • Выполняет вычисления
  • Становится на паузу в ожидании следующей задачи (возврат к первому пункту списка)
Кроме того:
  • Все три потока должны начинать работу "одновременно" (это ограничение само появляется из следующего требования)
  • Каждый поток должен дожидаться пока остальные два потока не выполнят работу
  • Управляющий поток должен дожидаться пока все вычислительные потоки не встанут на паузу и только после этого раздавать им задачи
  • Каждый поток должен быть создан только один раз
  • Производительность решения очень важна!
Пытаясь решить эту задачу я написал следующий тестовый пример:
    class Program
    {
        static AutoResetEvent mt = new AutoResetEvent(false);
        static AutoResetEvent th1 = new AutoResetEvent(false);
        static AutoResetEvent th2 = new AutoResetEvent(false);
        static AutoResetEvent th3 = new AutoResetEvent(false);

        static Thread mainThread;
        static Thread thread1;
        static Thread thread2;
        static Thread thread3;

        static void Main(string[] args)
        {
            mainThread = new Thread(MainThread);
            mainThread.IsBackground = true;

            thread1 = new Thread(Thread1);
            thread1.IsBackground = true;

            thread2 = new Thread(Thread2);
            thread2.IsBackground = true;

            thread3 = new Thread(Thread3);
            thread3.IsBackground = true;

            thread1.Start();
            thread2.Start();
            thread3.Start();
            mainThread.Start();

            Console.ReadLine();
        }

        static void MainThread()
        {
            while (true)
            {
                Console.Write("\nT ");

                if ((thread1.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread2.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin ||
                    (thread3.ThreadState & ThreadState.WaitSleepJoin) != ThreadState.WaitSleepJoin)
                {
                    mt.WaitOne();
                }

                if ((thread1.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread2.ThreadState & ThreadState.Running) == ThreadState.Running ||
                    (thread3.ThreadState & ThreadState.Running) == ThreadState.Running)
                {
                    th1.Set();
                    th2.Set();
                    th3.Set();
                }

                Thread.Sleep(1000);
            }
        }

        static void Thread1()
        {
            while (true)
            {
                mt.Set();
                th1.WaitOne();
                Console.Write("1");
            }
        }

        static void Thread2()
        {
            while (true)
            {
                mt.Set();
                th2.WaitOne();
                Console.Write("2");
            }
        }

        static void Thread3()
        {
            while (true)
            {
                mt.Set();
                th3.WaitOne();
                Console.Write("3");
            }
        }
    }

И всё вроде работает, но есть две вещи которые меня не устраивают:
  • Наличие большого количества экземпляров AutoResetEvent
  • Необходимость выполнять проверку состояния потока через ThreadState
Не устраивают они меня потому, что пока нужно только три "вычислительных" потока с этим можно жить, а представьте во что это превратиться если их будет пару десятков.
Поэтому решил спросить Вас, есть ли способ более красиво решить требуемую задачу?