Search code examples
c#multithreadingwinformsthread-safetyconcurrent-queue

Worker Threads Blocking When ConcurrentQueue has too many items


This is a weird one, I have a Thread[] of worker threads which each process items in a ConcurrentQueue<string> until the queue is empty, at which point the rest of the program continues.

This works until about ~1500 items at which point all threads stay blocked in the WaitSleepJoin state and never process any of the items in the queue.

I've tried stepping through my code and it appears that the threads are still created, still started and are alive but get blocked immediately and never run their relevant function.

I'm completely flummoxed so any help would be appreciated!

The relevant sections of code are below:

Main Thread Segment:

            ConcurrentQueue<string> convertionQueue = new ConcurrentQueue<string>();
            List<Thread> converterThreads = new List<Thread>();
            Directory.GetFiles(_folderOne, "*.fdf", SearchOption.AllDirectories).ToList().ForEach(file => convertionQueue.Enqueue(file));
            Directory.GetFiles(_folderTwo, "*.fdf", SearchOption.AllDirectories).ToList().ForEach(file => convertionQueue.Enqueue(file));
            int filesDone = 0;
            int totalFiles = convertionQueue.Count;
            progressBar.Maximum = totalFiles;
            panel1.Visible = true;
            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                converterThreads.Add(new Thread(() => ConvThreadWorker(convertionQueue, ref filesDone)));
            }
            converterThreads.ForEach(thread => thread.Start());
            DateTime lastTick = DateTime.Now;
            int lastFilesDone = 0;
            int[] valuesSpeed = { 1, 1, 1, 1, 1 };
            int[] valuesTime = { 1, 1, 1, 1, 1 };
            int counter = 0;
            while (converterThreads.Any(thread => thread.IsAlive))
            {

                TimeSpan t = DateTime.Now - lastTick;
                int deltaFiles = filesDone - lastFilesDone;
                double speed = (float)t.TotalMilliseconds <= 0.0 ? 0.0 : deltaFiles / (float)t.TotalMilliseconds;
                double tMinus = speed <= 0 ? 0.0 : (totalFiles - filesDone) / speed;
                int currentSpeed = (int)(speed * 1000);
                int currentTime = (int)(tMinus / 1000);
                valuesSpeed[counter] = currentSpeed;
                valuesTime[counter] = currentTime;
                lblFilesLeft.Text = string.Format("{0}/{1}", filesDone, totalFiles);
                lblSpeed.Text = valuesSpeed.Sum() / 5 + " /s";
                lblTime.Text = valuesTime.Sum() / 5 + " s";
                lblFilesLeft.Update();
                lblSpeed.Update();
                lblTime.Update();
                progressBar.Value = filesDone;
                progressBar.Update();
                lastTick = DateTime.Now;
                lastFilesDone = filesDone;
                counter = ++counter % 5;
                Thread.Sleep(500);
            }

Worker Function:

private void ConvThreadWorker(ConcurrentQueue<string> queue, ref int fileCounter)
{
    while (!queue.IsEmpty)
    {
        string file;
        if (queue.TryDequeue(out file))
        {
            ConvToG(file);
            fileCounter++;
        }
    }
}

Convertion Function:

private void ConvToG(string file)
{
    MessageBox.Show("Entering Convertion Function");
    if (!_fileCreationDictionary.ContainsKey(file))
    {
        DateTime lastTimeModified = File.GetLastWriteTime(file);
       _fileCreationDictionary.AddOrUpdate(file, lastTimeModified, (key,oldvalue)=>lastTimeModified);
    }
    ProcessStartInfo procStart = new ProcessStartInfo
    {
        Arguments = file,
        UseShellExecute = true,
        FileName = Fdfg,
        WindowStyle = ProcessWindowStyle.Hidden
    };
    Process process = new Process {StartInfo = procStart};
    MessageBox.Show("Starting convertion process");
    process.Start();
    process.WaitForExit();
    MessageBox.Show("Finished");
}

The confusing part appears to be how this all revolves around the number of items in the queue, yet there appears to be no overflow.

UPDATE: Adding the mbox's shows that it freezes on the process.Start() section of code, with no errors and will not proceed past that point.

UPDATE 2: If UseShellExecute = false the code works. Which is very confusing to say the least.


Solution

  • I have done something similar with threads spawning processes to collate data. I had issues around the actual process starting and hanging. What I did to get my program working was something like this:

    using (Process process = Process.Start(startInfo)) {
        if(process.WaitForExit(timeOutMilliseconds)) {
            MessageBox.Show("Process exited ok");
            //...snip
        } else {
            MessageBox.Show("Process did not exit in time!");
            //...snip
            process.Kill();
        }
    }
    

    There is a bit more going on in the background, regarding limiting the number of running process, etc, but I found that occasionally, for an unknown reason, that I would see several process in the task manager just hanging around forever.

    Hope that helps?