Tech Off Thread

3 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

Having trouble mutithreading

Back to Forum: Tech Off
  • User profile image
    Shining Arcanine

    An application I'm writing to find large prime numbers currently starts an instance of backgroundWorker which does calculations in the background then returning the final result to an eventhandler. Since my processor has hyperthreading (and I plan to upgrade to dualcore), I'd like to mutithread this. I've been trying to mutithread it but I've had some difficulty. No matter what I do, my processor's CPU utilization is at 50% (and therefore it is probably executing one thread). Here is what I've written:

    Shining Arcanine wrote:
    private BigInteger findPrime(UInt64 pow, UInt64 b)
    {

        BigInteger number = 8;
        BigInteger last = 0;
        BigInteger until;
        UInt64 exponent = 3;
        int pcount = 2;

        BigInteger lastPrime = 3;
        ManualResetEvent[] doneEvents = new ManualResetEvent[pcount];
        mprime[] mprimeArray = new mprime[pcount];

        until = calculate.pow(b, pow);

        while (last <= until)
        {

            for (int i = 0; i < pcount; i++)
            {
                doneEvents[i] = new ManualResetEvent(false);
                mprime f = new mprime(doneEvents[i], number - 1, exponent);
                mprimeArray[i] = f;
                ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);

                number <<= 1;
                exponent++;
            }

            WaitHandle.WaitAll(doneEvents);

            for (int i = 0; i < pcount; i++)
            {
                if (mprimeArray[i].result)
                {
                    lastPrime = mprimeArray[i].number;
                }

                last = mprimeArray[i].number;
            }
        }

        return lastPrime;
    }


    Does anyone have any ideas as to what I did wrong?

  • User profile image
    Mike Dimmick

    That bit looks OK, what does mprime.ThreadPoolCallback look like? If you're getting poor scalability you should probably check that your lock overhead (if any) isn't exceeding the amount of time actually spent on the work - and that the work items aren't actually finishing before the next one is queued up.

    With only one work item queued, actually, you may well find that the ThreadPool does not create an additional thread to run the items in parallel. The ThreadPool is smart enough to ration the number of threads to prevent too much context-switch overhead - I'm not sure if it uses the GetQueuedCompletionStatus API to use I/O completion ports (which causes the OS to keep the number of runnable threads fairly close to the number of logical processors available). So you could actually queue up to 64 work items in your innermost loop - this is a limitation of WaitForMultipleObjects which currently supports a maximum of 64 objects at once. That would allow much more to happen concurrently before the thread which runs findPrime needs to be woken up to schedule more work.

    I'm trying to think if there's a better way to handle the queuing of work without queuing pow - 3 work items. Perhaps using a Semaphore object that you initialise to some high-water mark - let's say 64 for the sake of argument - and have a loop which uses WaitOne to gain a single resource - this call will block when all resources are exhausted. Then you have your work item callback call Release on the semaphore when it's finished, allowing the main thread to queue another work item. You still have to deal with what happens when the work is exhausted, i.e. the work item that deals with the last range has been queued, since you can't rely on the last work item being the one that finishes last even though it was queued last. You'd probably also need to keep the array of ManualResetEvents in order to track when all of the final 64 work items have completed.

    I note that you're leaking ManualResetEvents, by the way. They do need to be Disposed (OK, the GC will clean them up when they're finalized but they will end up surviving from generation 0, and the finalizer thread will need to run). You can reuse them simply by calling Reset - there's no need to create new ones every time around the for loop which creates work items.

    I have a rough idea for how the loop should go in my head but I've not worked it out fully.

  • User profile image
    Shining Arcanine

    Thanks for the information Mike. I've been busy with exams so I didn't remember to check back until now. Here is mPrime.ThreadPoolCallBack:

    public void ThreadPoolCallback(Object threadContext)
    {
        _threadIndex = (int)threadContext;
        _result = calculateMersennePrime(_number, _exponent);
        _doneEvent.Set();
    }

    Regarding ThreadPool, this program is supposed to use as many resources as it can so it runs as quickly as possible so its resource conservative behavior is a bit of a problem. Is there any way to make it more aggressive?

    By the way, I'm new to programming so I didn't know that I was leaking ManualResetEvents. Exactly how would one fix that?

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.