public OutputDTO(InputDTO dto)
 {
     data   = dto.Data;
     RowNbr = dto.RowNbr;
 }
 public void putToWorkerQ(InputDTO dto)
 {
     lock (workerQueue) {
         workerQueue.Enqueue(dto);
     }
 }
        public void Orchestrate()
        {
            performInit();
            openDBConnection();
            int counter = 0;
            int burst   = 0;
            int wqLen   = 0;

            // ////////////////////////////////////////////
            workerQueue = new ConcurrentQueue <InputDTO>();
            resultQueue = new ConcurrentQueue <ServiceResult>();
            workers     = new List <MultiThreadWorker>(maxThreads);
            for (int i = 0; i < nbrThreadsInit; i++)
            {
                workers.Add(new MultiThreadWorker("Worker " + i, workerQueue, resultQueue));
            }

            // ////////////////////////////////////////////

            long currentTime   = CommonConstants.currentTimeMillis();
            long startTime     = currentTime;
            int  timeDelta     = 0;
            int  saveTimeDelta = 0;

            while (!stopping)
            {
                // we should come through here once per second
                // almost1Second is exactly that - if we construct the loop for exactly 1 second,
                // we go past that value by as much as 35 ms
                // recalculate it here, because we might adjust the housekeepingSleepTimeValue
                almost1Second = 1000 - housekeepingSleepTimeValue + 1;

                // read dbOutput requestsPerSecond rows at a time, and apportion to workers
                // if the workerQueue contains rows, read less rows to ensure the queue total
                // will be the configured batch size
                try {
                    List <String> rows = performQuery(wqLen);                 // could set stopping = true

                    Console.WriteLine(CommonConstants.currentTimeExtended() + ": read " + rows.Count + " qLen = " + wqLen);
                    burst = rows.Count + getWorkerQLength();                 // how many we are trying to process this second
                    if (StopFileExists(serviceStopFile))
                    {
                        stopping = true;                       // only set flag to allow orderly shutdown
                    }
                    for (int i = 0; i < workers.Count; i++)
                    {
                        workers.ElementAt(i).currentTime = currentTime;
                    }
                    if (rows.Count > 0)
                    {
                        for (int i = 0; i < rows.Count; i++)
                        {
                            counter++;
                            InputDTO dto = new InputDTO((String)rows.ElementAt(i), counter);
                            putToWorkerQ(dto);
                            if (i > 0 && i % workers.Count == 0)
                            {
                                Thread.Sleep(queueLoadingPauseTimeValue);
                            }
                        }
                    }
                    currentTime   = CommonConstants.currentTimeMillis();
                    saveTimeDelta = 0;
                    if (!workersStarted)
                    {
                        // set startTime again for closer approx of actual time to process batch
                        startTime = CommonConstants.currentTimeMillis();
                        for (int i = 0; i < workers.Count; i++)
                        {
                            workers.ElementAt(i).start();
                        }
                        workersStarted = true;
                    }
                    else
                    {
                        for (int i = 0; i < workers.Count; i++)
                        {
                            // start any new ones
                            if (!workers.ElementAt(i).isRunning())
                            {
                                workers.ElementAt(i).start();
                            }
                            else
                            {
                                // reset currentTime for workers
                                // they can then sleep for the balance of the second if queue goes empty
                                workers.ElementAt(i).currentTime = currentTime;
                            }
                        }
                    }

                    // allow the workers to do some work
                    Thread.Sleep(waitForWorkersTime);                 // this should not be made too small

                    timeDelta = (int)(CommonConstants.currentTimeMillis() - currentTime);

                    //************************************//
                    while (timeDelta < almost1Second)
                    {
                        // this is our housekeeping loop
                        // process the output from the workers, update the database
                        if (allWorkersIdle())
                        {
                            if (saveTimeDelta == 0)
                            {
                                saveTimeDelta = timeDelta;     // used to calculate burst rate
                            }
                            if (!stopping && !tooManyWorkers && timeDelta < processingTooFastTime)
                            {
                                tooManyWorkers = true;
                                Console.WriteLine(CommonConstants.currentTimeExtended() + ": too many workers @ " + timeDelta);
                            }
                        }
                        if (getResultQLength() > 0)
                        {
                            // process 1 entry in our input q
                            processResultQItem();
                        }
                        else
                        {
                            Thread.Sleep(housekeepingSleepTimeValue);
                        }
                        timeDelta = (int)(CommonConstants.currentTimeMillis() - currentTime);
                    }
                    //************************************//

                    // almost 1 second has elapsed.

                    if (stopping)                    // not a panic abort, but orderly shutdown
                    {
                        while (!allWorkersIdle() || getResultQLength() > 0)
                        {
                            if (getResultQLength() > 0)
                            {
                                // process 1 entry in our input q
                                processResultQItem();
                            }
                            else
                            {
                                Thread.Sleep(housekeepingSleepTimeValue);
                            }
                        }
                        // if we get here, all Qs are empty
                        // we can now shut down the workers
                        updateTimestamp = CommonConstants.currentTimeMillis();
                        long shutdownAbort = updateTimestamp + maxShutdownTimeValue;
                        for (int i = 0; i < workers.Count; i++)
                        {
                            workers.ElementAt(i).stop();
                        }
                        Thread.Sleep(300);
                        bool threadsStillRunning = true;
                        while (threadsStillRunning)
                        {
                            if (CommonConstants.currentTimeMillis() > shutdownAbort)
                            {
                                for (int i = 0; i < workers.Count; i++)
                                {
                                    if (workers.ElementAt(i).isRunning())
                                    {
                                        workers.ElementAt(i).stopNow();
                                    }
                                }
                            }
                            else
                            {
                                threadsStillRunning = false;
                                for (int i = 0; i < workers.Count; i++)
                                {
                                    if (workers.ElementAt(i).isRunning())
                                    {
                                        threadsStillRunning = true;
                                    }
                                }
                            }
                            if (threadsStillRunning)
                            {
                                Thread.Sleep(100);
                            }
                        }
                    }
                    else
                    {
                        // not stopping, almost 1 second elapsed.
                        // do we need more or less workers?
                        wqLen  = getWorkerQLength();
                        burst -= wqLen;
                        if (saveTimeDelta != 0)
                        {
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": processed " + burst + " items in " + saveTimeDelta + " ms, loop time=" + timeDelta);
                        }
                        else
                        {
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": processed " + burst + " items in " + timeDelta + " ms");
                            saveTimeDelta = timeDelta;
                        }
                        if (wqLen > 0 || saveTimeDelta > processingTooSlowTime)
                        {
                            needMoreWorkers = true;
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": more workers needed Q = " + wqLen + ", tDelta=" + saveTimeDelta);
                        }
                        if (needMoreWorkers)
                        {
                            int nbrNeeded = wqLen / workers.Count;
                            if (nbrNeeded == 0)
                            {
                                nbrNeeded = 1;
                            }
                            for (int i = 0; i < nbrNeeded; i++)
                            {
                                if (workers.Count < maxThreads)
                                {
                                    // do not start them until we give them work
                                    int n = workers.Count;
                                    MultiThreadWorker mtw = new MultiThreadWorker("Worker " + n, workerQueue, resultQueue);
                                    workers.Add(mtw);
                                }
                            }
                            needMoreWorkers = false;
                            tooManyWorkers  = false;
                        }
                        if (tooManyWorkers)
                        {
                            if (workers.Count > minThreads && saveTimeDelta > 0 && wqLen == 0)
                            {
                                // remove 1 worker
                                int n = workers.Count - 1;
                                MultiThreadWorker mtw = workers.ElementAt(n);
                                workers.RemoveAt(n);
                                mtw.stop();                 // this allows it to shut down in an orderly fashion
                                                            // and return its result if it is processing
                            }
                            tooManyWorkers = false;
                        }
                    }
                } catch (ThreadInterruptedException ex) {
                    Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread was interrupted : " + ex.Message);
                }
            }
            closeDBConnection(StopFileExists(serviceStopFile));
            if (StopFileExists(serviceStopFile))
            {
                System.IO.File.Delete(serviceStopFile);
            }

            long   deltaT  = updateTimestamp - startTime;
            double decSecs = ((double)deltaT) / 1000;
            int    seconds = (int)(deltaT / 1000);
            double average = Math.Floor(((double)counter * 1000) / ((double)deltaT) * 100) / 100;               // per second 2 decimal places

            Console.WriteLine(CommonConstants.currentTimeExtended() + ":  #### - Stopping - processed " + counter + " requests in " + decSecs + " seconds" +
                              " ==> average = " + average + " per second");
        }
        ///////////////////////////////////////////////
        /// work is done here
        ///

        public void doWork()
        {
            Random        rand   = new Random();
            InputDTO      dto    = null;
            ServiceResult res    = null;
            OutputDTO     outDTO = null;

            running = true;
            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Running " + threadName);
            try {
                currentTime = CommonConstants.currentTimeMillis();
                while (!stopping)
                {
                    int pauseTime = rand.Next(71) + 170; // random web request time 170..240
                    if (!workerQueue.IsEmpty)
                    {
                        // prevent blocking loop by checking Q length first
                        dto = getFromWorkerQ();   // may return null
                        if (dto != null)
                        {
                            // simulate processing = Let the thread sleep for a while.
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread: " + threadName + ", row " + dto.RowNbr + ", Qlen = " + getQLength() +
                                              ", working for " + pauseTime);
                            Thread.Sleep(pauseTime);
                            // send back the results
                            outDTO = new OutputDTO(dto);
                            res    = new ServiceResult(CommonConstants.statusSuccess, outDTO);
                            putToResultQ(res);
                        }
                    }
                    if (getQLength() == 0)
                    {
                        long endTime = CommonConstants.currentTimeMillis();
                        int  deltaT  = (int)(endTime - currentTime);
                        if (deltaT < 100)   // less than a tenth second has elapsed
                        {
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread: " + threadName + ", Qlen = " + getQLength() + ", sleeping for " + sleepAdj);
                            Thread.Sleep(sleepAdj);
                        }
                        else if (deltaT < almost1Second)     // less than a second has elapsed
                        {
                            sleepTime = 1000 - deltaT - sleepAdj;
                            if (sleepTime < 0)
                            {
                                sleepTime = smallSleepTime;
                            }
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread: " + threadName + ", Qlen = " + getQLength() + ", sleeping for " + sleepTime);
                            Thread.Sleep(sleepTime);
                        }
                        else
                        {
                            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread: " + threadName + ", Qlen = " + getQLength() + ", elapsed " + deltaT + ", sleeping for " + smallSleepTime);
                            Thread.Sleep(smallSleepTime);
                        }
                        //currentTime = System.currentTimeMillis();
                    }
                }
            } catch (ThreadInterruptedException e) {
                Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread " + threadName + " interrupted.");
            }
            Console.WriteLine(CommonConstants.currentTimeExtended() + ": Thread " + threadName + " exiting.");
            running = false;
        }