Должен ли я использовать потоки или задачи - множественное моделирование клиентов

Я пишу программу моделирования клиентов, в которой все моделируемые клиенты выполняют некоторую предопределенную процедуру против сервера - это веб-сервер, работающий в лазурном режиме с четырьмя экземплярами.

Все имитируемые клиенты запускают одну и ту же процедуру после подключения к серверу.

В любое время я хотел бы моделировать от 300 до 800 клиентов, используя мою программу.

Мой вопрос: Должен ли я создавать N экземпляров класса клиентов и запускать их в N разных потоках? ИЛИ

Должен ли я использовать библиотеку задач для выполнения этих задач?

Ответ 1

Вы, конечно же, не должны создавать 800 потоков.

Давайте сделаем шаг назад. У вас есть устройство, называемое "сервером", которое принимает "запросы" от "клиентов" и отправляет "ответы" обратно этим клиентам. Предположим, что запросы - это кусочки бумаги, доставленные почтовым отделением, а ответы - это коробки, содержащие книги, также доставленные почтовым отделением.

Вы хотите смоделировать 800 клиентов, чтобы протестировать сервер.

Предположим, что поток - это человек, а процессор - стул. Человек может выполнять только работу, сидя в кресле.

Создание 800 потоков - это эквивалент выхода и найма 800 человек, и каждый из них должен отправить письмо на сервер. Но у вас только четыре стула, поэтому эти 800 человек должны по очереди использовать стулья.

Это было бы смехотворным решением в реальной жизни. Нитки, как и люди, безумно дороги. Вы должны минимизировать количество создаваемых потоков.

Итак, следует ли вместо 800 задач выполнять задачу factory и позволить TPL распараллелить их для вас?

Нет, вы тоже не должны этого делать. У TPL есть пул людей (нитей), чтобы рисовать, и он пытается устроить так, чтобы в платежной ведомости больше людей, чем есть стулья для них, чтобы сидеть. Но ваша задача не "связана с креслом" - - люди собираются сидеть в кресле, отправлять запрос на сервер, а затем убираться со стула, пока они ждут ответа, чтобы вернуться. Пока они ждут, TPL теперь должен нанять больше людей для обслуживания дополнительных задач.

Нахождение веб-сервера связано с привязкой ввода-вывода; вы должны создавать задачи с пулом для задач, которые связаны с ЦП.

Правильное решение - нанять двух человек.

Один человек - "поток завершения ввода-вывода" - ничего не делает, кроме как отправлять запросы в почтовый ящик и проверять входящие пакеты. Другой человек - человек "симуляции" - выясняет, какой правильный "график" предназначен для имитации 800 клиентов. Человек-симулятор разрабатывает расписание, а затем ложится спать. Она просыпается, когда пришло время отправить еще один запрос на сервер. Когда она просыпается, она сообщает, что поток завершения ввода-вывода отбрасывает это письмо в почтовом ящике и разбудит ее, когда приходит ответ. Затем она возвращается спать, пока не придет время отправить другой запрос или ответ приходит, что необходимо проверить.

То, что вам нужно сделать, это либо (1) получить бета-версию С# 5 и использовать async/await для создания задач, отправляющих запросы на сервер, а затем вернуть управление в цикл сообщений до тех пор, пока не наступит время для отправки другой запрос или ответ. Или, если вы не хотите использовать С# 5, вы должны создать источник завершения задачи и настроить задачи, которые имеют правильные продолжения.

Вкратце: правильный способ обработки многих параллельных задач ввода-вывода состоит в том, чтобы создать очень небольшое количество потоков, каждый из которых выполняет очень небольшое количество работы за раз. Пусть поток завершения ввода-вывода обрабатывает детали ввода-вывода. Вам не нужно нанимать 800 человек, чтобы имитировать отправку 800 писем. Наймите двух человек, один, чтобы посмотреть почтовый ящик, и один, чтобы написать письма.

Ответ 2

Ответ в этом случае не так прост. Это действительно зависит от того, как вы хотите, чтобы ваши клиенты моделировались:

  • Если вы хотите подключить 800 клиентов, но не обязательно в то же время, рекомендуется использовать Task s. Они легки и эффективно используют базовый ThreadPool.

  • Если вы действительно хотите, чтобы клиенты были абсолютно все параллельно, я боюсь, что нет способа избежать потоков. Нет волшебного способа получить 800 облегченных одновременных выполнения задач. Абстракция Task является легкой, потому что она использует пул потоков. Это означает, что многие задачи сопоставляются с небольшим количеством реальных потоков. Но, конечно, это означает, что они действительно не работают параллельно, но вместо этого планируются для запуска, когда это возможно. ThreadPool имеет максимальное количество потоков 250 (AFAIK), поэтому не более 250 "клиентов" будут выполняться за один раз, если вы используете Task s. Решение задает максимальные потоки до 800, но в этот момент это то же самое, что и при использовании классических потоков.

Ответ 3

Я бы использовал библиотеку задач и позволил библиотеке задач обрабатывать все потоки для вас. Вы не хотите разворачивать 800 потоков. Его плохая идея, чтобы иметь много одновременных потоков, происходящих одновременно, вот еще один вопрос, который говорит об этом: Максимальное количество потоков в приложении .NET?

Ответ 4

Для этого домены приложений - ваш лучший выбор.

Домен приложения - это среда выполнения, в которой выполняется приложение .NET. Он обеспечивает границу управляемой памяти, контейнер для параметров конфигурации приложения, а также обеспечивает интерфейс связи для распределенных приложений.

Каждое приложение .NET обычно содержит только один домен приложения, который автоматически создается CLR при запуске данного процесса/программы. Иногда полезно (в таком случае, как ваше) создавать дополнительные области приложений с помощью одного процесса/программы. Использование нескольких доменов приложений позволяет избежать осложнений связи и возникать с использованием нескольких отдельных процессов и обеспечивает изоляцию ваших задач.

Для чего вы хотите иметь два варианта.

  • Запустите X потоков в отдельном потоке в том же домене.

Это будет означать, что вам будет очень надоело быть потокобезопасным, что будет очень сложно с такой задачей, как симуляция нескольких логинов, имитация клиентов и т.д.

  • Начать потоки X в том же процессе каждый в своем собственном домене приложения.

Это позволит изолировать каждый из открученных потоков, а также легко получить доступ к хостинговому приложению/программе. Имея все вы X-моделирование в X отдельных доменах приложений, каждый домен будет изолирован и не сможет вмешиваться в другое моделирование клиента через статические члены класса и т.д.

Далее приводится выдержка из книги Иосифа Альбахари С# 4.0 В двух словах, которую я настоятельно рекомендую:

Пример 40 одновременных симуляций клиентов может вам пригодиться:

class program
{
    static void main()
    {
        // Create 40 domains and 40 threads.
        AppDomain[] domains = new AppDomain[40];
        Thread[] thread = new Thread[40];

        for (int i = 0; i < 40; i++)
        {
            domains[i] = AppDomain.CreateDomain("Client Simulation " + i);
            thread[i] = new Thread(SimulateClientInOtherDomain);
        }

        // Start all threads, passing to each thread its app domain.
        for (int j = 0; j < 40; j++)
            threads[j].Start(domains[j]);

        // Wait for the threads to finish.
        for (int k = 0; k < 40; k++)
            threads[k].Join();

        // Unload the application domains.
        for (int l = 0; l < 40; l++)
            AppDomain.Unload(domains[l]);
    }

    // Thread start with input of with domain to run on/in.
    static void SimulateClientInOtherDomain(object domain)
    {
        ((AppDomain)domain).DoCallBack(Simulate);
    }

    static void Simulate()
    {
       Client simClient1 = new Client("Bill", "Gates", ...);
       simClient1.Simulate();
    }
}

Надеюсь, это поможет.