First of all - Happy New Year. Hope everyone had a great end to 2014 and has a great start to 2015.
I have a question regarding threading and how the framework handles threads because I am completely befuddled by some behavior I am seeing with my code and I have spent the last several days trying to track this down but can’t figure it out.
I am using Aaron Balman’s Thread Pool Manager class to handle a number of different threaded objects in code. For those who don’t know, to spin a thread up in this class, you assign an interface to your object that has a method in it called “DoWork.” You then send your object to a queue method in the thread pool manager where it gets assigned to a thread and the thread calls the DoWork method.
My code is also using the MBS Bonjour classes.
So what happens is that I have devices that appear on the local network. The bonjour classes detect them and resolve their IP addresses. Then objects are created in code that are code representations of the real world devices. I then send these objects to the thread pool manager. The threads then take care of logging into the devices, and getting parameters about the devices such as the device’s MAC address, firmware version, hardware features, etc. I then display these objects in a listbox. I have chosen a threading model for connecting to the hardware as it’s much faster with a large number of devices. I may have 20 or 30 or more devices and the UI is just not locked up as the devices are all having their information read.
What’s happening is that sometimes, I seem to be seeing multiple threads being spawned for the same device when there should only be one. I can see this in the debugger. Now I know that with bonjour a device can be discovered multiple times. So I am making sure that I’m only using one bonjour lookup and ignoring any others that come in for the same device. There’s only one place where the “queue” method of the thread pool manager is called and from what I can see, the queue method is only being called once. But I’m getting all these seemingly duplicate threads.
So does the framework sometimes split threads on its own? Does it create new instances of a thread during something like a recursive call to a method (which I use sometimes in the threaded code) or during a call to sleep or another context switch? I’m really stumped on this. It doesn’t happen all the time either.
I think I have something in my code somewhere that’s not working right but I can’t find it. So I’m asking the experts here if maybe there’s something the framework is doing…
I’m not sure if you’re aware of this, but threads now have a DebugIdentifier property which you can assign some meaningful string to and then the debugger will show that in the threads popup when you’ve stopped in the debugger.
Sounds really strange. Maybe with system logging you can write our thread ID when a thread starts and also a thread ID when it finishes.
Also when you enqueue a job. Maybe you find a case where your code inserts job twice.
Kem - that’s what I was looking for - confirmation that the framework does not spawn or split threads.
Joe - hey thanks for the tip on the debugidentifier property. That’s definitely something I’ll look into with this.
And yeah, I’ve got some system.debuglog statements. I’ll have to try to find more.
The biggest problem with the thread pool manager is that since it’s an “obscured” (not sure if that’s the right term) item, it’s really hard to see where the calls to launch the thread game from. I’ll have to look at adding code to the threadpoolmanager object to insert this identifier…
I also wonder if you might not be better off with a Timer instead. Each iteration of the Timer would peel one device off an array, process it, then add it to a “processed” Dictionary to prevent it from being processed again. If you set the Period to something low, it should happen in near real time and the UI would be relatively unaffected. You’ll end up with an easier to manage and maintain system.
Not sure. Some systems using the software could have maybe 50 or 60 devices or more. Threading vs. not threading makes a big difference. I’m not sure if processing it serially would be better than processing in parallel. It’s a thought…
The interesting thing is that when the app first starts and devices get discovered - everything works great. It’s when I do an update of the devices such as changing their IP addresses and then rebooting them that it all starts to get wonky. When I do that, I remove the bonjour lookup item (my devices are kept in a dictionary and don’t get removed upon reboot) and then when the lookup appears again, we check to see if we have the matching device in the dictionary. If we do, then we assign the device and do the lookups and launch the thread. Otherwise we create a new device and do the same.
So it’s something that’s hanging around or not getting cleaned up. It’s very strange…
The interesting thing is that in OS X, the threading happens so fast and smooth, I pretty much would not have caught this. It was some funky stuff that I was seeing in Windows that lead me to this discover. Threading in Windows is not nearly as forgiving as in OS X.
I’ll consider the timer approach, but it would be a very major change and this is pretty much the last big issue to clear up before I can release the update to my app…
OK. Interesting stuff. I just added debugging identifiers in the threads and what appears to be happening is that my bonjour lookup items are not being removed. I keep a dictionary of them as they get added in the discovery process and I remove them from the dictionary when a device reboots or goes away. I would think that would get rid of it, but I must be holding a reference to the Bonjour lookup some place else…
So basically I have two bonjour lookup items doing the same thing…I can tell that as there are two calls with nearly the exact same debug identifier string but I’m adding in the handle of the object making the call and those handles are different…
Christian - does your plugin code for DNSServiceResolveMBS retain references to those items some place?
I think I may have discovered my references to the bonjour lookups that were staying around.
I used some AddHandler directives as I don’t have the DNSServiceResolveMBS objects dropped on the window in the IDE. Instead I create them all in code and use the AddHandler directives to set up the events.
I never issued any RemoveHandler calls before attempting to remove or close the DNSServiceResolveMBS object. That would still keep a reference around - right? Since the framework has that handler identified.
Yes, that appears to be the source of my problem. I added in the RemoveHandler directives and now things seem to be working correctly. I’m getting only one thread spun up for each device. I knew it had to be something like that as when I first start the program - it all worked correctly. Now when I remove one of the bonjour lookup objects, I remove the handlers first. With those lookups hanging around, they’d launch the threads as well…
Note to self: REMOVE YOUR HANDLERS!!!
Closing an object does NOT break that handler reference…