.Net: Фоновый рабочий и несколько процессоров - PullRequest
3 голосов
/ 08 декабря 2011

Я использую BackgroundWorker для выполнения некоторых тяжелых операций в фоновом режиме, чтобы пользовательский интерфейс не переставал отвечать на запросы.

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

Можно ли использовать все процессоры с BackgroundWorker?

enter image description here

Вот мой упрощенный код, только если вам интересно!


private System.ComponentModel.BackgroundWorker bwPatchApplier;

this.bwPatchApplier.WorkerReportsProgress = true;
this.bwPatchApplier.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bwPatchApplier_DoWork);
this.bwPatchApplier.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bwPatchApplier_ProgressChanged);
this.bwPatchApplier.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bwPatchApplier_RunWorkerCompleted);

private void bwPatchApplier_DoWork(object sender, DoWorkEventArgs e)
{
    string pc1WorkflowName;
    string pc2WorkflowName;

    if (!GetWorkflowSettings(out pc1WorkflowName, out pc2WorkflowName)) return;

    int progressPercentage = 0;
    var weWorkspaces = (List<WEWorkspace>) e.Argument;

    foreach (WEWorkspace weWorkspace in weWorkspaces)
    {
        using (var spSite = new SPSite(weWorkspace.SiteId))
        {
            foreach (SPWeb web in spSite.AllWebs)
            {
                using (SPWeb spWeb = spSite.OpenWeb(web.ID))
                {
                    PrintHeader(spWeb.ID, spWeb.Title, spWeb.Url, bwPatchApplier);

                    try
                    {
                        for (int index = 0; index < spWeb.Lists.Count; index++)
                        {
                            SPList spList = spWeb.Lists[index];

                            if (spList.Hidden) continue;

                            string listName = spList.Title;

                            if (listName.Equals("PC1") || listName.Equals("PC2"))
                            {
                                #region STEP 1

                                // STEP 1: Remove Workflow

                                #endregion

                                #region STEP 2

                                // STEP 2: Add Events: Adding & Updating

                                #endregion
                            }

                            if ((uint) spList.BaseTemplate == 10135 || (uint) spList.BaseTemplate == 10134)
                            {
                                #region STEP 3

                                // STEP 3: Configure Custom AssignedToEmail Property

                                #endregion

                                #region STEP 4

                                if (enableAssignToEmail)
                                {
                                    // STEP 4: Install AssignedTo events to Work lists
                                }

                                #endregion
                            }

                            #region STEP 5

                            // STEP 5 Install Notification Events

                            #endregion

                            #region STEP 6

                            // STEP 6 Install Report List Events

                            #endregion

                            progressPercentage += TotalSteps;
                            UpdatePercentage(progressPercentage, bwPatchApplier);
                        }
                    }
                    catch (Exception exception)
                    {
                        progressPercentage += TotalSteps;
                        UpdatePercentage(progressPercentage, bwPatchApplier);
                    }
                }
            }
        }
    }

    PrintMessage(string.Empty, bwPatchApplier);
    PrintMessage("*** Process Completed", bwPatchApplier);

    UpdateStatus("Process Completed", bwPatchApplier);
}

Большое спасибо за внимание:)

Ответы [ 6 ]

5 голосов
/ 08 декабря 2011

BackgroundWorker работает в одном фоновом потоке (ThreadPool). Таким образом, если он сложен в вычислительном отношении, он будет интенсивно использовать один процессор. Поток пользовательского интерфейса все еще работает на втором, но, вероятно, (как и большинство пользовательских интерфейсов) тратит почти все время простоя, ожидая ввода (что хорошо).

Если вы хотите разделить свою работу, чтобы использовать более одного процессора, вам нужно будет использовать некоторые другие методы. Это может быть несколько BackgroundWorker компонентов, каждый из которых выполняет определенную работу, или напрямую использующий ThreadPool. Параллельное программирование было упрощено в .NET 4 через TPL, что, вероятно, является очень хорошим вариантом. Подробнее см. мои серии на странице MSDN TPL или в библиотеке параллельных задач .

3 голосов
/ 08 декабря 2011

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

Было бы заманчиво создать метод, принимающий аргумент SPWeb, и просто вызывать Thread.Start () снова и снова для каждого объекта;затем закончите с Thread.Join () или WaitAll (), чтобы дождаться их завершения в конце BackgroundWorker.Однако это будет плохой идеей, потому что вы потеряете эффективность, поскольку операционная система тратит время на переключение контекста между всеми потоками.

Вместо этого вы хотите, чтобы ваша система работала только в нескольких потоках, но как минимум в двух (в данном случае).Хорошее эмпирическое правило: (2n - 1), где «n» - это количество процессорных ядер, которые у вас есть ... но есть все виды случаев, когда вы хотите нарушить это правило.Вы можете реализовать это с помощью ThreadPool, перебирая объекты SPWeb и добавляя их в очередь, из которой вы продолжаете извлекать, или другими способами, такими как TPL.

3 голосов
/ 08 декабря 2011

Каждый BackgroundWorker использует только один поток, чтобы выполнить то, что вы ему сказали. Чтобы использовать преимущества нескольких ядер, вам потребуется несколько потоков. Это будет означать либо несколько BackgroundWorkers , либо порождение нескольких потоков из вашего DoWork метода.

1 голос
/ 08 декабря 2011

BackgroundWorker запускает новый поток на втором ядре ЦП, оставляя интерфейс отзывчивым.

Если вы используете .NET 4, изучите возможность использования параллельной библиотеки задач , которая может дать вам лучшие результаты и использовать оба ядра.

0 голосов
/ 08 декабря 2011

В этом есть потенциальные подводные камни, но вы можете получить некоторое преимущество от использования Parallel.ForEach:

Вместо

foreach (SPWeb web in spSite.AllWebs)
{
    //Your loop code here
}        

Вы могли бы:

Parallel.Foreach(spSite.AllWebs, web =>
        {
          //Your loop code here
        });

Это в основном создает задачу (из API задач в .NET 4.0) из каждого элемента и планирует работу с TaskPool, что даст вам некоторый параллелизм, который вам понадобится для использования этих ядер.

Вам придется исправить неизбежные проблемы параллелизма, которые могут возникнуть из-за этого, но это хорошая отправная точка. Вы по крайней мере исправите тот факт, что вы поддерживаете общее состояние между потоками (счетчик хода выполнения). Вот несколько советов по этому поводу: http://msdn.microsoft.com/en-us/library/dd997392.aspx

0 голосов
/ 08 декабря 2011

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

...