VB.NET:线程函数调用比直接调用函数慢?

时间:2010-09-14 22:46:59

标签: .net vb.net multithreading delegates

我有一个函数,我在Sub中执行大量的数据库操作。直接调用时的操作如:

ExecProcess()

需要大约11秒才能运行。

但是,如果我创建一个Delegate,并使用BeginInvoke()调用sub,则执行相同的过程需要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 个答案:

答案 0 :(得分:2)

  

任何人都知道可能导致功能的原因   在新线程中花费更长时间,   而不是直接打电话?

所有条件都相同当代码使用配置为在单线程单元(STA)中运行并满足以下其中一个条件的COM对象时,会发生从一个线程到另一个线程执行速度较慢的最常见原因之一

  • 从实例化它之外的线程调用它。
  • 从配置为在多线程单元(MTA)中运行的线程调用它。

昂贵的编组操作大部分发生在对象的每次访问中。慢4倍是这个问题的一个完全合理的症状。如果继续使用BeginInvoke调用机制,解决此问题将非常困难。原因是因为该机制使用ThreadPool线程执行,不能轻易(或根本)切换到STA模式。

我认为您最好的选择是创建自己的线程池,您可以在其中控制公寓状态。它并不像听起来那么难。以下代码使用.NET 4.0中提供的BlockingCollection数据结构或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);

但最重要的是要记住,必须在STA线程上实例化COM对象,并且必须从该线程进行所有进一步的访问。任何偏离都将导致编组操作。如果您选择在此答案中使用该方法,则必须在委托中创建COM对象。

答案 1 :(得分:1)

正如Henk所指出的那样,你真的只是使用常规ThreadPool的包装器,所以所有正常的线程规则都适用。我强烈建议你寻找一个锁争用或其他两个线程正在访问相同信息并争夺谁拥有它的情况。

如果那不起作用......

有一些担心除非调用EndInvoke,否则BeginInvoke会导致资源泄漏。那么,线程或后续线程的第一次产生是否会长期启动?这对我来说曾经是一个问题,但它并不总是一个问题。它最终会在没有EndInvoke的情况下获得GC,但不会立即。

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

编辑:还有一些想法:您是否使用不同线程(包括主线程)的相同连接?或者,您的连接池是否在可用连接(服务器端或客户端端)之外运行你最终阻止等待可用的连接?

此外,有时人们会对其数据访问进行编码,因此他们只能通过使事务处于静态/共享状态来使每个事务处于活动状态。这将是另一个需要寻找的东西。