I'm kinda lost on this one ; i've tried everything i know for doing such operation and the error persists.
I've a FileProcessor class that creates a new thread, do some operations, etc ; however, even when manually calling Dispose() inside it and Thread.Interrupt() i can't seem to delete the files after use.
First i was doing this code using an async method on the main thread ; now i've switched to threading with this FileProcessor, just trying to delete those files after the operation.
I can delete one or two files, but when it gets to the third file it throws an System.IOEXception
I truly don't know what else can i do. Any input is appreciated.
I was using Worker.Join inside Dispose() and waiting for the thread to finish or the GC ends it - but neither of em ever happened.
Thanks
My code (reduced as possible)
Form1:
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private bool RestartTimer;
private bool ThreadRunning;
FileProcessor TIFFtoXMLProcessor;
FileProcessor CIP3toTIFFProcessor;
List<string> files;
public Form1()
{
InitializeComponent();
TIFFtoXMLProcessor = new FileProcessor();
RestartTimer = false;
}
private void BeginWork()
{
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile1.txt");
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile2.txt");
TIFFtoXMLProcessor.EnqueueFileName(#"C:\test\yourtestfile3.txt");
files = new List<string>(TIFFtoXMLProcessor.fileNamesQueue);
TIFFtoXMLProcessor.eventWaitHandle.Set();
if(TIFFtoXMLProcessor.worker.IsAlive == false)
{
foreach(var item in files)
{
System.IO.File.Delete(item);
}
}
}
}
}
The FileProcessor class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
namespace WindowsFormsApp1
{
class FileProcessor : IDisposable
{
public EventWaitHandle eventWaitHandle { get; private set; }
public Thread worker { get; private set; }
private readonly object locker = new object();
public Queue<string> fileNamesQueue { get; private set; }
public string currConversion { get; private set; }
public bool JobComplete { get; private set; }
private CancellationTokenSource cancelParallelWorker;
public string ColorSeparator { get; private set; }
private readonly TextBox tbStatus;
public string outputFolder { get; private set; }
List<string> filesgoingtorun;
//var AvailableJobsDictionary = new Dictionary<string, List<string>>();
//string nZones, string zWidth, string fzWidth, string lzWidth, string zAreaWidth, string zAreaHeight, double DPI
public FileProcessor()
{
eventWaitHandle = new AutoResetEvent(false);
fileNamesQueue = new Queue<string>();
// Create worker thread
worker = new Thread(Work)
{
IsBackground = true
};
cancelParallelWorker = new CancellationTokenSource();
worker.Start();
}
public void EnqueueFileName(string FileName)
{
// Enqueue the file name
// This statement is secured by lock to prevent other thread to mess with queue while enqueuing file name
lock (locker) fileNamesQueue.Enqueue(FileName);
// Signal worker that file name is enqueued and that it can be processed
//eventWaitHandle.Set();
}
private void Work()
{
List<string> filesToWork = new List<string>();
while (true)
{
string fileName = null;
// Dequeue the file name
lock (locker)
while (fileNamesQueue.Count > 0)
{
fileName = fileNamesQueue.Dequeue();
filesToWork.Add(fileName);
if (fileName == null) return;
}
if (fileNamesQueue.Count == 0 && filesToWork.Count > 0)
{
var tempList = new List<string>(filesToWork);
filesToWork.Clear();
ProcessJob(tempList);
}
}
}
private void ProcessJob(List<string> filesToWork)
{
try
{
JobComplete = true;
switch (currConversion)
{
case "TIF":
{
int j = 0;
foreach (var currJob in filesToWork)
{
//Series of tasks...
j++;
}
eventWaitHandle.WaitOne();
break;
}
}
JobComplete = false;
Dispose();
}
catch (Exception conversionEx)
{
cancelParallelWorker?.Cancel();
}
}
#region IDisposable Members
public void Dispose()
{
// Signal the FileProcessor to exit
EnqueueFileName(null);
// Wait for the FileProcessor's thread to finish
worker.Interrupt();
// Release any OS resources
eventWaitHandle.Close();
}
#endregion
}
}
Your code is insanely complex for what you're trying to do and it's no wonder that somewhere you've left a handle for a file open on a different thread and that's preventing your code from being able to delete the file. Without being able to replicate the issue at this end I can even begin to figure out what you should do.
But here's the approach I'm going to suggest.
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do something like this:
public partial class Form1 : Form
{
private Subject<string> _enqueue = new Subject<string>();
private IDisposable _subscription = null;
public Form1()
{
InitializeComponent();
string ColorSeparator = "42";
int imageRotationNumber = 42;
IObservable<string> query =
from file in _enqueue
from ImageListSorted in Observable.Start(() => ImageBuilder(file, ColorSeparator))
from RotateCMYK in Observable.Start(() => Rotate(ImageListSorted.CMYKmages, imageRotationNumber))
select file;
_subscription = query.Subscribe(f => System.IO.File.Delete(f));
_enqueue.OnNext(#"C:\test\yourtestfile1.txt");
_enqueue.OnNext(#"C:\test\yourtestfile2.txt");
_enqueue.OnNext(#"C:\test\yourtestfile3.txt");
}
private CreateCMYKAndImpositionImageList ImageBuilder(string JobImages, string colorDelimiter)
{
return new CreateCMYKAndImpositionImageList(JobImages, colorDelimiter);
}
private RotateImages Rotate(Dictionary<string, string> imageList, int RotationNumber)
{
return new RotateImages(imageList, RotationNumber);
}
}
Now, I've only included two steps in your process, but you should be able to continue the logic through the rest of the steps.
Each step is run asynchronously and the entire thing can be cancelled anytime by calling _subscription.Dispose();.
The final .Subscribe(f => System.IO.File.Delete(f)) can only be hit once all of the steps are complete.
So as long as you avoid anything relating to threading and tasks then this should run quite cleanly.
Related
I was recently asked a thread safety question in a c# interview. I didn't get it quite right.
I'm am trying to understand it now.
Here's the question...
Given a basic Order class...
using System;
namespace OrderThreadTest
{
public class Order
{
public TimeSpan CancelTime;
public int Id;
}
}
Given a simple schedule class stub however I have implemented it to test at home...
using System;
using System.Threading;
namespace OrderThreadTest
{
public class Scheduler : IDisposable
{
private Timer _timer;
public IDisposable Schedule(Order order, Action action)
{
var current = DateTime.Now;
var timeToGo = order.CancelTime - current.TimeOfDay;
_timer = new Timer(x => { action(); }, null, timeToGo, Timeout.InfiniteTimeSpan);
return this;
}
public void Dispose()
{
}
}
}
How do you ensure that the Cancel method in the worker class is thread safe?
My answer is the commented out pieces
using System;
namespace OrderThreadTest
{
public class Answer
{
private readonly Scheduler _scheduler;
private object _myLock = new object();
private Order _order;
public Answer(Scheduler scheduler)
{
_scheduler = scheduler;
}
public void Cancel(Order order)
{
// lock (_myLock)
// {
// _order = order;
// var result =
_scheduler.Schedule(order, () =>
{
//if (order.Equals(_order))
//{
Console.WriteLine("Canceled: " + order.Id);
order = null;
//}
});
// }
}
}
}
My first question in understanding this is how can I cause an example of a second thread setting the passed in Order and changing a earlier timer?
For example cause a thread data clash.
I have tried like this but it always seems to run as expected...
using System;
namespace OrderThreadTest
{
internal class Program
{
private static void Main(string[] args)
{
var a1 = new Answer(new Scheduler());
var o = new Order
{Id = 1, CancelTime = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second + 5)};
a1.Cancel(o);
a1.Cancel(o);
Console.ReadLine();
}
}
}
How do I repro the problem that I am meant to solve here?
I have an application that works quite slow and I'm trying to speed it up.
I am quite new to concurrent systems, so I'm a bit stuck here.
Shortly, I can present the system as the following classes:
Some resource that is being processed
public class Resource
{
public int Capacity { get; set; } = 1000;
}
A consumer
public class Consumer
{
private readonly int _sleep;
public Consumer(int sleep)
{
_sleep = sleep;
}
public void ConsumeResource(Resource resource)
{
var capture = resource.Capacity;
Thread.Sleep(_sleep); // some calsulations and stuff
if (resource.Capacity != capture)
throw new SystemException("Something went wrong");
resource.Capacity -= 1;
}
}
And resource manager that does the job
public class ResourceManager
{
private readonly List<Consumer> _consumers;
private readonly Resource _resource;
public ResourceManager(List<Consumer> consumers)
{
_consumers = consumers;
_resource = new Resource();
}
public void Process()
{
Parallel.For(0, _consumers.Count, i =>
{
var consumer = _consumers[i];
consumer.ConsumeResource(_resource);
});
}
}
So as you saw, Consumer relies on Resource state. If you run this simulation with the following code
static void Main(string[] args)
{
var consumers = new List<Consumer>
{
new Consumer(1000),
new Consumer(900),
new Consumer(800),
new Consumer(700),
new Consumer(600),
};
var resourceManager = new ResourceManager(consumers);
resourceManager.Process();
}
you will see that when capacity of resource changes, everything breaks.
I couldn't think of any other example, and it lacks a couple of details.
First, there are many instances of Resource class, so locking access
to it would not discard all efforts to make the code concurrent.
Second, in real application this problem is very rare, so I can
sacrifice a bit of performance there.
I'm guessing this issue can be fixed with properly placed locks, but I fail to place them correctly.
As I understand the concept of locks, it prevents the locked code from being called from different threads simultaneously. Placing lock in Consumer::ConsumeResource would not help, just as placing it inside Resource::Capacity setter. I need somehow to lock modification of a resource while a consumer is doing it's job with the resource.
I hope I explained my problem efficiently. It is all quite new to me, so I'll try to make things more concrete if needed.
After thinking long and hard, I conjured a somewhat sloppy solution.
I decided to lock Resource property for a consumer using comsumer's id, and manually wait for next consumer's turn:
public class Resource
{
private int Capacity { get; set; } = 1000;
private Guid? _currentConsumer;
public int GetCapacity(Guid? id)
{
while (id.HasValue && _currentConsumer.HasValue && id != _currentConsumer)
{
Thread.Sleep(5);
}
_currentConsumer = id;
return Capacity;
}
public void SetCapacity(int cap, Guid id)
{
if (_currentConsumer.HasValue && id != _currentConsumer)
return;
Capacity = cap;
_currentConsumer = null;
}
}
public class Consumer
{
private readonly int _sleep;
private Guid _id = Guid.NewGuid();
public Consumer(int sleep)
{
_sleep = sleep;
}
public void ConsumeResource(Resource resource)
{
var capture = resource.GetCapacity(_id);
Thread.Sleep(_sleep); // some calsulations and stuff
if (resource.GetCapacity(_id) != capture)
throw new SystemException("Something went wrong");
resource.SetCapacity(resource.GetCapacity(_id) - 1, _id);
}
}
This way it works as expected, but I get a feeling that it also can be implemented with locks.
After some research about locks and stuff, I wrote that little helper class:
public class ConcurrentAccessProvider<TObject>
{
private readonly Func<TObject> _getter;
private readonly Action<TObject> _setter;
private readonly object _lock = new object();
public ConcurrentAccessProvider(Func<TObject> getter, Action<TObject> setter)
{
_getter = getter;
_setter = setter;
}
public TObject Get()
{
lock (_lock)
{
return _getter();
}
}
public void Set(TObject value)
{
lock (_lock)
{
_setter(value);
}
}
public void Access(Action accessAction)
{
lock (_lock)
{
accessAction();
}
}
}
With that, I rewrote Resource and Consumer in order to make it thread-safe:
public class Resource
{
public ConcurrentAccessProvider<int> CapacityAccessProvider { get; }
private int _capacity;
public Resource()
{
CapacityAccessProvider = new ConcurrentAccessProvider<int>(() => _capacity, val => _capacity = val);
}
public int Capacity
{
get => CapacityAccessProvider.Get();
set => CapacityAccessProvider.Set(value);
}
}
public class Consumer
{
private readonly int _sleep;
public Consumer(int sleep)
{
_sleep = sleep;
}
public void ConsumeResource(Resource resource)
{
resource.CapacityAccessProvider.Access(() =>
{
var capture = resource.Capacity;
Thread.Sleep(_sleep); // some calsulations and stuff
if (resource.Capacity != capture)
throw new SystemException("Something went wrong");
resource.Capacity -= 1;
Console.WriteLine(resource.Capacity);
});
}
}
In the provided example those manipulations effectively kill all possible profits from concurrency, but it is because there is only one Resource instance. In real world application when there are thousands of resources and only several conflicting cases, that will work just fine.
I have a Winform application that needs to manage several state machines concurrently. Each state machine may have a socket, serial port or other external connection.
Here is my state machine shell implemented using async/await and dataflow BlockBuffer as a message queue.
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
public class StateMachineMessage
{
private static int id = 1;
public int Id { get; } = 0;
public string Name { get; }
public string Text { get; }
public BufferBlock<StateMachineMessage> Queue { get; set; }
public StateMachineMessage(string name, string text)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = null;
}
public StateMachineMessage(string name, string text, BufferBlock<StateMachineMessage> queue)
{
Id = Interlocked.Increment(ref id);
Name = name;
Text = text;
Queue = queue;
}
public override string ToString()
{
return "(" + Id + ":" + Name + ":" + Text + ")";
}
}
public class StateMachine
{
private int State { get; }
public string Name { get; }
RichTextBox TextBox { get; }
BufferBlock<StateMachineMessage> Queue { get; }
public StateMachine(string name, BufferBlock<StateMachineMessage> queue, RichTextBox textBox)
{
Name = name;
Queue = queue;
TextBox = textBox;
}
public async Task StartAsync()
{
while (await Queue.OutputAvailableAsync())
{
var request = await Queue.ReceiveAsync();
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, request));
if (request.Queue != null)
{
var response = new StateMachineMessage("RESPONSE", "Good Morning!");
TextBox.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
Name, response));
await request.Queue.SendAsync(response);
}
}
}
}
The main form creates the dataflow block buffers and state machines on initialization.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace DataflowThread
{
public partial class Form1 : Form
{
// Enables the user interface to signal cancellation.
CancellationTokenSource cancellationSource;
// The worker state machine and dataflow node
StateMachine workerMachine;
BufferBlock<StateMachineMessage> workQueue;
// The main thread state machine
StateMachine mainMachine;
BufferBlock<StateMachineMessage> mainQueue;
// Enables progress bar actions to run on the UI thread.
TaskScheduler uiTaskScheduler;
public Form1()
{
InitializeComponent();
InitializeDataflow();
StartStateMachines();
}
public void InitializeDataflow()
{
// Create the cancellation source.
cancellationSource = new CancellationTokenSource();
// Create the UI task scheduler from the current sychronization context.
uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// Create dataflow options
var options = new ExecutionDataflowBlockOptions();
options.CancellationToken = cancellationSource.Token;
options.TaskScheduler = uiTaskScheduler;
// Create dataflow nodes for the main thread and worker state machines
mainQueue = new BufferBlock<StateMachineMessage>(options);
workQueue = new BufferBlock<StateMachineMessage>(options);
// Create the state machines for the worker and main thread
mainMachine = new StateMachine("MainMach", mainQueue, richTextBox1);
workerMachine = new StateMachine("WorkerMach", workQueue, richTextBox2);
}
public void StartStateMachines()
{
var mainTask = mainMachine.StartAsync();
var workerTask = workerMachine.StartAsync();
}
~Form1()
{
cancellationSource.Dispose();
}
private void sendButton_Click(object sender, EventArgs e)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
private async void cancelButton_Click(object sender, EventArgs e)
{
try
{
workQueue.Complete();
mainQueue.Complete();
await Task.WhenAll(workQueue.Completion, mainQueue.Completion);
richTextBox1.AppendText("All queues Completed\n");
resetButton.Enabled = true;
}
catch (OperationCanceledException ex)
{
richTextBox1.AppendText(ex.ToString());
}
}
private void sendTenButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i ++)
{
var request = new StateMachineMessage("REQUEST", "Hello World!", mainQueue);
richTextBox1.AppendText(string.Format("{0}: {1}: {2}\n",
Thread.CurrentThread.ManagedThreadId,
mainMachine.Name, request));
workQueue.Post(request);
}
}
private void resetButton_Click(object sender, EventArgs e)
{
resetButton.Enabled = false;
InitializeDataflow();
StartStateMachines();
richTextBox1.Clear();
richTextBox2.Clear();
}
}
}
One started each state machine loops on the BlockBuffer until cancelled. My question:
What is the correct async/await usage for the outermost StartStateMachines() method?
Silently ignoring the Tasks returned from each state machine start feels funny. I am relatively new to C# and am trying to use the async / await patterns where I would normally use a message queue and thread. I am also trying to be a bit modern and not use Invoke/BeginInvoke everywhere. Any help is appreciated.
Thanks in advance.
Sean
EDIT - I added the full state machine source and Program.cs here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DataflowThread
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
I have a ListBox that is bound to a BindingList. The BindingList is built up when a third party application raises an event. I can see the BindingList being bound correctly... but nothing enters the ListBox. I have used the exact same logic with some of my own custom types and it usually works very well.
Form class
private Facade.ControlFacade _controlFacade;
public UavControlForm()
{
InitializeComponent();
_controlFacade = new UavController.Facade.ControlFacade();
UpdateEntityListBox();
}
private void UpdateEntityListBox()
{
lsbEntities.DataSource = _controlFacade.GetEntityTally();
lsbEntities.DisplayMember = "InstanceName";
}
Facade class
private Scenario _scenario;
public ControlFacade()
{
_scenario = new Scenario();
}
public BindingList<AgStkObject> GetEntityTally()
{
BindingList<AgStkObject> entityTally = _scenario.EntityTally;
return entityTally;
}
Scenario class
private static BindingList<IAgStkObject> _entityTally = new BindingList<AgStkObject>();
public Scenario()
{
if (UtilStk.CheckThatStkIsAvailable())
{
UtilStk.StkRoot.OnStkObjectAdded += new IAgStkObjectRootEvents_OnStkObjectAddedEventHandler(TallyScenarioObjects);
UtilStk.StkRoot.OnStkObjectDeleted += new IAgStkObjectRootEvents_OnStkObjectDeletedEventHandler(TallyScenarioObjects);
}
}
private void TallyScenarioObjects(object sender)
{
List<AgStkObject> tallyOfStkObjects = UtilStk.GetRunningTallyOfAllStkObjects();
List<string> stkObjectNames = UtilStk.GetInstanceNamesOfStkObjects(tallyOfStkObjects);
foreach (string stkObjectName in stkObjectNames)
{
if (!SearchFlightUavTallyByName(stkObjectName))
{
if (!SearchLoiterUavTallyByName(stkObjectName))
{
if (!SearchEntityTallyByName(stkObjectName))
{
int i = stkObjectNames.IndexOf(stkObjectName);
_entityTally.Add(tallyOfStkObjects[i]);
}
}
}
}
}
I can see the event fire from the third-party application - this adds an entity to _entityList as desired, but noothing is added to lsbEntities - why?
(jump right to the last example if you want to see it fixed etc)
Threads and "observer" patterns (such as the data-binding on winforms) are rarely good friends. You could try replacing your BindingList<T> usage with the ThreadedBindingList<T> code I used on a previous answer - but this combination of threads and UI is not an intentional use-case of winforms data-binding.
The listbox itself should support binding via list notification events (IBindingList / IBindingListView), as long as they arrive form the right thread. ThreadedBindingList<T> attempts to fix this by thread-switching on your behalf. Note that for this to work you must create the ThreadedBindingList<T> from the UI thread, after it has a sync-context, i.e. after it has started displaying forms.
To illustrate the point that listbox does respect list-change notifications (when dealing with a single thread):
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
using (var timer = new Timer())
{
var data = new BindingList<Foo>();
form.Controls.Add(lst);
lst.DataSource = data;
timer.Interval = 1000;
int i = 0;
timer.Tick += delegate
{
data.Add(new Foo(i++));
};
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
timer.Start();
};
Application.Run(form);
}
}
}
and now with added threading / ThreadedBindingList<T> (it doesn't work with the regular BindingList<T>):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
{
form.Controls.Add(lst);
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
BindingList<Foo> data = new ThreadedBindingList<Foo>();
lst.DataSource = data;
ThreadPool.QueueUserWorkItem(delegate
{
int i = 0;
while (true)
{
data.Add(new Foo(i++));
Thread.Sleep(1000);
}
});
};
Application.Run(form);
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
ctx = SynchronizationContext.Current;
}
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
ctx.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}
For a "log information for support" type of function I'd like to enumerate and dump active thread information.
I'm well aware of the fact that race conditions can make this information semi-inaccurate, but I'd like to try to get the best possible result, even if it isn't 100% accurate.
I looked at Process.Threads, but it returns ProcessThread objects, I'd like to have a collection of Thread objects, so that I can log their name, and whether they're background threads or not.
Is there such a collection available, even if it is just a snapshot of the active threads when I call it?
ie.
Thread[] activeThreads = ??
Note, to be clear, I am not asking about Process.Threads, this collection gives me a lot, but not all of what I want. I want to know how much time specific named threads in our application is currently using (which means I will have to look at connecting the two types of objects later, but the names is more important than the CPU time to begin with.)
If you're willing to replace your application's Thread creations with another wrapper class, said wrapper class can track the active and inactive Threads for you. Here's a minimal workable shell of such a wrapper:
namespace ThreadTracker
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
public class TrackedThread
{
private static readonly IList<Thread> threadList = new List<Thread>();
private readonly Thread thread;
private readonly ParameterizedThreadStart start1;
private readonly ThreadStart start2;
public TrackedThread(ParameterizedThreadStart start)
{
this.start1 = start;
this.thread = new Thread(this.StartThreadParameterized);
lock (threadList)
{
threadList.Add(this.thread);
}
}
public TrackedThread(ThreadStart start)
{
this.start2 = start;
this.thread = new Thread(this.StartThread);
lock (threadList)
{
threadList.Add(this.thread);
}
}
public TrackedThread(ParameterizedThreadStart start, int maxStackSize)
{
this.start1 = start;
this.thread = new Thread(this.StartThreadParameterized, maxStackSize);
lock (threadList)
{
threadList.Add(this.thread);
}
}
public TrackedThread(ThreadStart start, int maxStackSize)
{
this.start2 = start;
this.thread = new Thread(this.StartThread, maxStackSize);
lock (threadList)
{
threadList.Add(this.thread);
}
}
public static int Count
{
get
{
lock (threadList)
{
return threadList.Count;
}
}
}
public static IEnumerable<Thread> ThreadList
{
get
{
lock (threadList)
{
return new ReadOnlyCollection<Thread>(threadList);
}
}
}
// either: (a) expose the thread object itself via a property or,
// (b) expose the other Thread public methods you need to replicate.
// This example uses (a).
public Thread Thread
{
get
{
return this.thread;
}
}
private void StartThreadParameterized(object obj)
{
try
{
this.start1(obj);
}
finally
{
lock (threadList)
{
threadList.Remove(this.thread);
}
}
}
private void StartThread()
{
try
{
this.start2();
}
finally
{
lock (threadList)
{
threadList.Remove(this.thread);
}
}
}
}
}
and a quick test driver of it (note I do not iterate over the list of threads, merely get the count in the list):
namespace ThreadTracker
{
using System;
using System.Threading;
internal static class Program
{
private static void Main()
{
var thread1 = new TrackedThread(DoNothingForFiveSeconds);
var thread2 = new TrackedThread(DoNothingForTenSeconds);
var thread3 = new TrackedThread(DoNothingForSomeTime);
thread1.Thread.Start();
thread2.Thread.Start();
thread3.Thread.Start(15);
while (TrackedThread.Count > 0)
{
Console.WriteLine(TrackedThread.Count);
}
Console.ReadLine();
}
private static void DoNothingForFiveSeconds()
{
Thread.Sleep(5000);
}
private static void DoNothingForTenSeconds()
{
Thread.Sleep(10000);
}
private static void DoNothingForSomeTime(object seconds)
{
Thread.Sleep(1000 * (int)seconds);
}
}
}
Not sure if you can go such a route, but it will accomplish the goal if you're able to incorporate at an early stage of development.
Is it feasible for you to store thread information in a lookup as you create each thread in your application?
As each thread starts, you can get its ID using AppDomain.GetCurrentThreadId(). Later, you can use this to cross reference with the data returned from Process.Threads.