Оптимизация пула потоков - PullRequest
1 голос
/ 10 марта 2011

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

Вот вопрос: во-первых, у меня есть кнопка «Пуск» для приложения, которое запускает Windows.Компонент Forms.Timer.

private void btnStart_MouseClick(object sender, MouseEventArgs e)
{
    timerNewCampaignChecker.Tick += new EventHandler(timerNewCampaignChecker_Tick);
    timerNewCampaignChecker.Enabled = true;
    timerNewCampaignChecker.Start();
}

Приложению необходимо каждые 3 секунды проверять базу данных на наличие новых кампаний (1-й метод), настраивать сведения о кампании (2-й), встроенные в пользовательский ввод, и отправлять кампанию получателям черезэлектронная почта (3-й).Чтобы проверять базу данных каждые 3 секунды на наличие новых кампаний, у меня есть вышеуказанный таймер (интервал 3 секунды), который запускает поток, проверяя новые кампании:

MethodInvoker invoker;
private void timerNewCampaignChecker_Tick(object sender, EventArgs e)
{
    invoker = new MethodInvoker(CheckNewCampaigns);
    invoker.BeginInvoke(null, null);
}

Итак, таймер запускает метод CheckNewCampaigns в каждых 3секунд и пусть процесс начнется с первого шага. Правильно ли запускать поток и структуру ThreadPool из самого таймера ??? Теперь CheckNewCampaigns проверяет БД, создает объект List и для каждой кампании отправляет сведения об объекте другому методу с помощьювызывая ThreadPool.

delegate bool StepCaller(int campaignID);
private void CheckNewCampaigns()
{   
    StringBuilder builder = new StringBuilder();
    StepCaller stepCaller = new StepCaller(PrepareCampaignEmail);
    IEnumerable<Campaigns> CampaignsList = DatabaseManager.GetCampaignsList(DatabaseManager.GetNewCampaigns());

    foreach (Campaigns Campaign in CampaignsList)
    {
        stepCaller.BeginInvoke(Campaign.CampaignID, new AsyncCallback(PrepareEmailCallback), null); 
    }
}

Должен ли я кодировать endInvoke в первом методе, или я должен просто запустить и забыть ?? .Поскольку наиболее важно то, что у приложения есть поток, параметры передаются между методами, 2-й метод должен знать, когда 1-й завершен, чтобы он мог взять campID и настроить его.Но при этом следует начинать новый поток и проверять новые кампании, что является 1-м методом.А также электронные письма должны быть отправлены в тот же момент.Новый метод (2-й) настраивает кампанию и вызывает другой метод (3-й) также с помощью ThreadPool.Вот оно:

private bool PrepareCampaignEmail(int campaignID)
    {
        EmailCaller emailCaller = new EmailCaller(SendEmail);

        IEnumerable<Subscribers> SubscribersList = DatabaseManager.GetCampaignSubscribersList(DatabaseManager.GetCampaignSubscribers(campaignID));

        CampaignCustomazition campaignCustomizer;
        List<CampaignCustomazition> campaignCustomizedList = new List<CampaignCustomazition>();  // foreach in içindeki beginInvoke ı dışarıya çıkardığımda kullanılacak

        foreach (Subscribers subscriber in SubscribersList)
        {
            campaignCustomizer = new CampaignCustomazition(campaignID);

            campaignCustomizer.CustomizeSource(campaignID, out campaignCustomizer.source, out campaignCustomizer.format);
            campaignCustomizer.CustomizeCampaignDetails(campaignID, out campaignCustomizer.subject, out campaignCustomizer.fromName, out campaignCustomizer.fromEmail, out campaignCustomizer.replyEmail);
            campaignCustomizer.CustomizeSubscriberDetails(campaignID, out campaignCustomizer.subscriberID, out campaignCustomizer.email, out campaignCustomizer.fullName);

            IAsyncResult result = emailCaller.BeginInvoke(campaignCustomizer, null, null);

            try
            {
                emailCaller.EndInvoke(result);
            }
            catch(Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }

        return false;
    }

Я полагаю, что в этом втором шаге есть главная проблема. Операция с базой данных выполняется почти 5-6 раз.Это может быть проблемой для темы, может быть.Кажется, работает нормально, но мой путь может быть неправильным.Там могут быть десятки кампаний в один день.Это проблема или у меня все нормально? Операции с БД не такие уж жесткие и сложные.

После подготовки письма оно готово к отправке, которое вызывается выше.Третий метод - это простой метод SMTP, который отправляет электронную почту и регистрирует ее.

Этот последний метод также не имеет функции EndInvoke, но это последний метод приложения в качестве только что отправленной почты.

Вам логичен мой способ работы с таймером и ThreadPool ??Как я могу проверить производительность наилучшим образом и оптимизировать работу Threadpool?Что вы можете предложить и научить любому из вышеперечисленного?

Большое спасибо

1 Ответ

2 голосов
/ 10 марта 2011

Прежде всего, вы должны знать, что .NET имеет множество вариантов таймера на выбор в зависимости от ваших потребностей.System.Windows.Forms.Timer используется, когда вы хотите, чтобы обратный вызов выполнялся в потоке пользовательского интерфейса (потому что вам нужно обновить пользовательский интерфейс любым способом).System.Timers.Timer и System.Threading.Timer выполняют обратный вызов в фоновом потоке.Вы можете прочитать больше об этом в этой статье MSDN .

. Вы всегда должны вызывать EndInvoke при использовании BeginInvoke для асинхронного выполнения делегата, как объяснено в документации .Для выполнения методов методом «забывай и забывай» рекомендуется использовать ThreadPool.QueueUserWorkItem (до .NET 4) или Task.TaskFactory.StartNew (.NET 4).Преимущество последнего заключается в том, что между потоками и задачами нет корреляции 1: 1 - планировщик выберет «подходящее» количество потоков для использования и запланирует задачи, которые будут выполняться в этих потоках.

Вывряд ли добьетесь максимальной производительности, просто порождая множество задач.Я бы создал собственный класс очереди на основе нового класса ConcurrentQueue (добавлен в .NET 4) и поместил в него исходящие электронные письма, а затем создал несколько задач для очистки очереди, доставляя почту на SMTP-сервер.ConcurrentQueue является поточно-ориентированным, поэтому вы можете безопасно добавлять и удалять элементы из нескольких потоков, используя его.

...