VB.NET: резьбовой вызов функции медленнее, чем прямой вызов функции? - PullRequest
1 голос
/ 15 сентября 2010

У меня есть функция, когда я выполняю большое количество операций с базой данных внутри Sub.Операция при прямом вызове выглядит следующим образом:

ExecProcess ()

занимает около 11 секунд.

Однако, если я создаю делегат и вызываю подпрограмму с помощью BeginInvoke (), тот же самый процесс занимает более 40 секунд.

Вот многопоточный код:

Protected del As ProcessDelegate
Protected Delegate Sub ProcessDelegate()
...           
del = New ProcessDelegate(AddressOf SELocal.ExecJob)
Dim cb As New AsyncCallback(AddressOf Me.ExecJobComplete)
del.BeginInvoke(cb, del)

Кто-нибудь знает, что может заставить функцию занять больше времени в новом потоке, чемзвонить напрямую?

Спасибо

Ответы [ 2 ]

2 голосов
/ 15 сентября 2010

Кто-нибудь знает, что может вызвать функцию занять больше времени в новой теме, а не звонить напрямую?

При прочих равных условиях одна из наиболее распространенных причин более медленного выполнения из одного потока в другой возникает, когда код использует COM-объект, настроенный для работы в однопотоковой квартире (STA), и удовлетворяет одному из следующих других условий. .

  • Он вызывается из потока, отличного от того, который его создал.
  • Вызывается из потока, настроенного для работы в многопоточной квартире (MTA).

Дорогая операция маршалинга чаще всего происходит при каждом доступе к объекту. Фактор в 4 раза медленнее является вполне разумным симптомом этой проблемы. Решить эту проблему будет довольно сложно, если вы продолжите использовать механизм вызова BeginInvoke. Причина в том, что этот механизм использует для выполнения поток ThreadPool, который не может быть легко (или вообще) переключен в режим STA.

Я думаю, что вам лучше всего создать собственный пул потоков, в котором у вас есть контроль над состоянием квартиры. Это не так сложно, как кажется. В следующем коде используется структура данных BlockingCollection , доступная в .NET 4.0 или как часть загрузки Reactive Extensions .

Примечание. Вам нужно будет добавить код, усиливающий себя, чтобы сделать его более надежным, поддерживать корректное отключение и т. Д.

public class CustomThreadPool
{
    private BlockingCollection<WorkItem> m_WorkItems = new BlockingCollection<WorkItem>();

    public CustomThreadPool(int poolSize)
    {
        for (int i = 0; i < poolSize; i++)
        {
            var thread = new Thread(Run);
            thread.IsBackground = true;
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
        }
    }

    public void QueueUserWorkItem(WaitCallback callback, object state)
    {
        m_WorkItems.Add(new WorkItem { Callback = callback, State = state });
    }

    private void Run()
    {
        while (true)
        {
            WorkItem item = m_WorkItems.Take();
            item.Callback(item.State);
        }
    }

    private class WorkItem
    {
        public WaitCallback Callback { get; set; }
        public object State { get; set; }
    }
}

И тогда вместо того, чтобы позвонить делегату BeginInvoke, вы сделаете это.

myThreadPool.QueueUserWorkItem((state) => { myDelegate(/* arguments */); }, null);

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

1 голос
/ 15 сентября 2010

Как указывает Хенк, вы на самом деле просто работаете с оберткой вокруг обычного ThreadPool, поэтому применяются все обычные правила потоков. Я НАСТОЯТЕЛЬНО рекомендую вам искать конфликт блокировки или другой случай, когда два потока обращаются к одной и той же информации и вступают в борьбу за то, кто ее имеет.

Если это не сработает ...

Существует некоторая обеспокоенность, что, если не вызывается EndInvoke, BeginInvoke может вызвать утечку ресурсов. Итак, долго ли запускается первое порождение потока или последующих потоков? Это было проблемой для меня когда-то в прошлом, но это не всегда проблема. В конечном итоге он получит GC без EndInvoke, но не сразу.

http://social.msdn.microsoft.com/forums/en-US/clr/thread/b18b0a27-e2fd-445a-bcb3-22a315cd6f0d/

РЕДАКТИРОВАТЬ : Еще несколько идей: используете ли вы одно и то же соединение с разными потоками (включая основной?) Или в вашем пуле соединений заканчиваются доступные соединения (на стороне сервера или клиента) и Вы заблокированы в ожидании доступного соединения?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...