views:

52

answers:

2

I have a function where I am performing a lot number of database operations inside a Sub. The operation when called directly like:

ExecProcess()

takes about 11 seconds to run.

However, if I create a Delegate, and call the sub using BeginInvoke(), the very same process takes over 40 seconds to execute.

Here's the threaded code:

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)

Anyone know what may cause a function to take longer inside a new thread, rather than calling directly?

Thanks

+1  A: 

As Henk points out, you really are just working with a wrapper around the usual ThreadPool, so all the normal threading rules apply. I would STRONGLY recommend you look for a lock contention or other case where two threads are accessing the same information and getting into a fight over who has it.

If that doesn't work...

There's some concern out there that unless EndInvoke gets called, BeginInvoke can cause resource leaks. So, is the long start up on the first spawning of the thread or subsequent threads? This was an issue for me once in the past, but it's not always an issue. It will eventually get GC'ed without the EndInvoke, but not immediately.

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

EDIT: Some more ideas: Are you using the same connection with different threads (including the main one?) Or, is your connection pool running out of available connections (either server or client side) and you end up blocked waiting for an available connection?

Also, sometimes folks code up their data access so they only can every have one transaction active by making the transaction static/shared. This would be something else to look for.

Jim Leonardo
I've debugged the code with timers etc to make sure it's not a spawn pause or anything like that.. the code runs.. just a lot slower. I'm not spawing multiple threads or anything like that, just the one call. I'm using a new connection within the call, however I am trying to share information back to the main thread (status info).. The method I'm calling is inside a class.. the class sets properties and I read them out as the status while the threaded method is executing. Could this somehow cause a lock contention?
aronh
Any attempt at accessing values between the two threads can cause contention. Further, it will be almost impossible to see when stepping through code. I'm not really a VB.Net guy, and there's no volatile keyword in VB.Net, but you should always be SyncLock-ing or otherwise ensuring you're synchronizing access of any objects being shared between threads.You could also have a look at this: http://stackoverflow.com/q/929146/195693
Jim Leonardo
+1  A: 

Anyone know what may cause a function to take longer inside a new thread, rather than calling directly?

All things being equal one of the most common causes of slower execution from one thread to another occurs when the code uses a COM object that is configured to run in a single thread apartment (STA) and meets one of the following other conditions.

  • It is called from a thread other than the one that instantiated it.
  • It is called from a thread configured to run in a multithreaded apartment (MTA).

An expensive marshaling operation most occur on each and every access to the object. A factor of 4x slower is a completely reasonable symptom of this issue. Resolving this issue will be quite difficult if you continue to use the BeginInvoke invocation mechanism. The reason is because that mechanism uses a ThreadPool thread for execution which cannot be easily (or at all) switched to STA mode.

I think your best bet is to create your own thread pool in which you do have control over the apartment state. It is not as hard as it sounds. The following code uses the BlockingCollection data structure is available in .NET 4.0 or as part of the Reactive Extensions download.

Note: You will have to add code hardening yourself to make it more robust, support gracefully shutting down, etc.

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; }
    }
}

And then instead of calling BeginInvoke on the delegate you would do this.

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

But the most important thing to remember is that the COM objects must be instantiated on an STA thread and all further access must be made from that thread. Any deviation from this will result in a marshaling operation. If you choose to use the approach in this answer then you will have to create the COM objects in the delegate.

Brian Gideon
I'm using a number of connection methods inside my threaded function, and also do in fact invoke COM objects and methods. How could I determine if this is in fact causing my problem?
aronh
+1 for pointing com
Sachin
@aronh: I added more information to my answer. The fact that you have acknowledge that you are using COM objects makes this the most likely cause of the slowness.
Brian Gideon