views:

232

answers:

4

I have a windows service that runs a method when the services main Timer elapses (OnElapse).

The OnElapse method gets a list of .xml files to process. Each xml file is inserted into a ThreadPool.

I want to make sure I don't insert 2 XML's with the same name into the ThreadPool.

How can I manage which items are in the ThreadPool? I basically want to do this:

if xmlfilename not in threadpool
   insert in threadpool
A: 

OnElaspe can't you rename the xml file ? so it has a unqiue name on the threadpool.

Ferdeen
Actually I don't want to add it to the pool i.e. Only process unique xml files at a time (business requirement)
Blankman
A: 

Couldn't you make a dictionary> and check that before you do the insertion? Something like this...

Dictionary<ThreadPool, List<String>> poolXmlFiles = new Dictionary<ThreadPool, List<String>>();
if(poolXmlFiles.ContainsKey(ThreadPool) && !poolXmlFiles[ThreadPool].Contains(xmlFileName))
{
     poolXmlFiles[ThreadPool].Add(xmlFileName);
     //Add the xmlFile to the ThreadPool
}

Sorry if there are syntax errors, I'm coding in VB these days.

AJ
but once the thread completes, how am I going to remove the filename from the dictionary? Is that threadsafe?
Blankman
It's threadsafe if you have the Dictionary defined in your service. You can get around using a ThreadPool for a key in your Dictionary by setting up another collection to relate your ThreadPools to a value-type, like an Integer. I'm assuming you know what the filename is when the thread completes?
AJ
Yes I do know the name when it completes, true!
Blankman
+3  A: 

This is pretty trick because you need to closely monitor the ThreadPool and it will require a form of synchronization. Here's a quick and dirty example of a way to do this.

class XmlManager {
  private object m_lock = new object();
  private HashSet<string> m_inPool = new HashSet<string>();

  private void Run(object state) {
    string name = (string)state;
    try { 
      FunctionThatActuallyProcessesFiles(name);
    } finally { 
      lock ( m_lock ) { m_inPool.Remove(name); }
    }
  }

  public void MaybeRun(string xmlName) { 
    lock ( m_lock ) { 
      if (!m_pool.Add(xmlName)) {
        return;
      }
    }
    ThreadPool.QueueUserWorkItem(Run, xmlName);
  }
}

This is not a foolproof solution. There is at least one race condition in the code. Namely that an item could be being removed from the pool while you're trying to add it back in and it won't actually get added. But if your are only concerned with them being processed a single time, this doesn't matter.

JaredPar
Wow, our solutions are remarkably similar but I like putting the remove in a finally block, it emphasizes the need to remove the item regardless of the outcome of the processing.
Stephen Martin
Also, I don't think I would call that a true race condition or even a flaw in the solution.
Stephen Martin
Actually another XML file won't hit the server until its finished processing, so the race condition won't ever happen. Thanks allot!
Blankman
@Stephen, it's definitely a race condition in certain environments. The OP says it doesn't apply to his though so it's OK.
JaredPar
+2  A: 

Something like this should do it (use a HashSet instead of a Dictionary if you are using .Net 3.5 or higher):

using System;
using System.Collections.Generic;
using System.Threading;

namespace Something
{
    class ProcessFilesClass
    {
        private object m_Lock = new object();
        private Dictionary<string, object> m_WorkingItems = 
            new Dictionary<string, object>();
        private Timer m_Timer;

        public ProcessFilesClass()
        {
            m_Timer = new Timer(OnElapsed, null, 0, 10000);
        }

        public void OnElapsed(object context)
        {
            List<string> xmlList = new List<string>();
            //Process xml files into xmlList

            foreach (string xmlFile in xmlList)
            {
                lock (m_Lock)
                {
                    if (!m_WorkingItems.ContainsKey(xmlFile))
                    {
                        m_WorkingItems.Add(xmlFile, null);
                        ThreadPool.QueueUserWorkItem(DoWork, xmlFile);
                    }
                }
            }
        }

        public void DoWork(object xmlFile)
        {
            //process xmlFile
            lock (m_Lock)
            {
                m_WorkingItems.Remove(xmlFile.ToString());
            }
        }
    }
}
Stephen Martin
<goes off to research HashSet> :-)
AJ