Кто-нибудь знает, что может вызвать функцию
занять больше времени в новой теме,
а не звонить напрямую?
При прочих равных условиях одна из наиболее распространенных причин более медленного выполнения из одного потока в другой возникает, когда код использует 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-объекты в делегате.