I would not use the background worker -- that ties your processing to the Winform UI layer. If you want to create a non-visual class that handles the threading and processing, you are best off using the Threadpool.
I would use the Threadpool vs. "straight" threads, as .Net will do some load balancing with the pool, and it recycles the threads so that you don't have to incur the cost of creating threads.
If you are using .Net 4, you might have a look at the new parallel threading library, I think it wrappers a lot of hte producer/consumer stuff.
You probably do want to use some sort of "throttle" to control how fast you are processing files (you probably don't want all 1000 files loaded into memory at once, etc). You might consider a producer/consumer pattern where you can control how many threads are processing at a time.
For thread-safe updates back to the UI, use the InvokeRequired and Invoke/BeginInvoke members on the Winforms controls.
Edit -- code example
My example is simpler than Lirik's but it doesn't do as much either. If you need a full producer/consumer, go with what Lirik wrote. From your question, it seems like you want to build a list of files, and them off to to some other component, and let those files be processed in the background. If that's all you want to do, you probably don't need a full producer/consumer.
I'm assuming that this is some sort of batch operation, and that once the user starts it, they will not be adding more files until the batch finishes. If that's not true, you might be better off with a producer/consumer.
This example can be used with a Winform, but you don't have to. You could use this component in a service, a console app, etc:
public class FileProcessor
{
private int MaxThreads = System.Environment.ProcessorCount;
private volatile int ActiveWorkers;
// you could define your own handler here to pass completion stats
public event System.EventHandler FileProcessed;
public event System.EventHandler Finished;
private readonly object LockObj = new object();
private System.Collections.Generic.Queue Files;
public void ProcessFiles(System.Collections.Generic.Queue files)
{
this.Files = files;
for (int i = 0; i < this.MaxThreads; i++)
System.Threading.ThreadPool.QueueUserWorkItem(this.ProcessFile);
}
private void ProcessFile(object state)
{
this.IncrementActiveWorkers();
string file = this.DequeueNextFile();
while (file != null)
{
this.DoYourWork(file);
this.OnFileProcessed(file);
file = this.DequeueNextFile();
}
// no more files left in the queue
int workers = this.DecrementActiveWorkers();
if (workers == 0)
this.OnFinished();
}
// please give me a name!
private void DoYourWork(string fileName) { }
private void IncrementActiveWorkers()
{
lock (this.LockObj)
{
this.ActiveWorkers++;
}
}
private int DecrementActiveWorkers()
{
lock (this.LockObj)
{
this.ActiveWorkers--;
return this.ActiveWorkers;
}
}
private string DequeueNextFile()
{
lock (this.LockObj)
{
// check for items available in queue
if (this.Files.Count > 0)
return this.Files.Dequeue();
else
return null;
}
}
private void OnFileProcessed(string fileName)
{
System.EventHandler fileProcessed = this.FileProcessed;
if (fileProcessed != null)
fileProcessed(this, System.EventArgs.Empty);
}
private void OnFinished()
{
System.EventHandler finished = this.Finished;
if (finished != null)
finished(this, System.EventArgs.Empty);
}
}
Since you said "specified files", I'm assuming that your Winform app has some sort of grid or listbox or other control that a user interacts with to select the files that are to be processed.
Here's an example of how to use it:
public class MyForm...
{
public void Go()
{
Queue files = new Queue();
// enqueue the name/path of all selected files into the queue...
// now process them
FileProcessor fp = new FileProcessor();
// example of using an event
fp.Finished += this.FileProcessor_Finished;
fp.ProcessFiles(files);
}
private void FileProcessor_Finished(object sender, System.EventArgs e)
{
// this event will have been called by a non-ui thread. Marshal it back to the UI
if(this.InvokeRequired)
this.Invoke(FileProcessor_Finished, new object[] {sender, e});
else
{
// handle the event -- this will be run on the UI thread.
}
}
}