There's a good deal of web chatter regarding VS/CLR threading creating memory management problems. Specifically, the issue seems to be that overactive threading creates noticeable memory leak issues.
I'm sorry to say that this is consistent with what I'm seeing as I add multi-threading to my current app.
For the guru's, attached please find a small project demonstrating the issue. It's pretty simple. It has one form, one button and one label. It has a class that manages a run/control loop and another that is run by the control loop. (This structure mimmicks my production code.) I suggest running it in the debugger. Don't know what it might perpetrate in the real world.
I'm currently running VS2008 under Vista in a 32 bit environment.
On my machine (a 2.4 gig, 4 core AMD with 16 gig mem - using only 4 of course), this locks after ~7k repeats (running the test is a good excuse to refresh the coffee input device). Not that there could be any doubt, but I am no longer any sort of kernel guru. However, using Process Explorer (http://technet.microsoft.com/en-us/s.../bb896653.aspx ), I was able to determine that in the end, this program dies trying to make a memory allocation associated with a thread creation. Between this and the fact that the run of the program shows a slight but noticeable upward trend in memory utilization, AND that my production program sucks down the memory like a black hole when running in multi thread mode (much bigger classes/routines), I conclude that memory is leaking.
My questions:
1) Do you have any suggestions for fixing the current style of thread use? (i.e. changing where the threads are declared, calling System.GC, doing something with Finalize, setting the thread to Nothing on completion, etc.) I've tried a number of these but I'm sure this audience will have more than I've thought of.
2) Does anyone know off hand if using a Threadpool mitigates this problem?
3) Any ideas (hopefully fairly simple) that would allow me to multi-thread (or process for that matter) that don't kill the OS environment.
If you really want to check this out and/or help and have only a 2005 environment, let me know and I'll build a version and upload it.
Thanks in advance.
PS. Please forgive in advance if there's a simple fix/explanation for this someplace. As I said above, I've googled this at some length and find a great many questions but a noticeable lack of answers. In that case, just point me to it!
Re: Threading and leaking memory - a demonstration
You can check out my IM class or my downloader class in my signature as they both involve multi-threading. I have not run into memory issue, but I need to read into your code a bit more...I'm not to sure about some things on it.
Re: Threading and leaking memory - a demonstration
The problem is a flaw in your code. I was able to reduce memory leakage drastically, essentially remove it, by simply adding the following line to your code:
Code:
For u As Integer = 1 To 10000
Dim x As Integer = 0
For Each POy As ProcessClass In ThreadCollection
If Not POy.t Is Nothing Then POy.t.Abort() <-- You have to abort the existing thread, as simply assigning the pointer 't' to a new thread will orphan existing threads that are not completed.
POy.t = New Thread(AddressOf POy.DoThreadWork)
POy.t.Start()
Next
...
Next
This is by no means the end all solution, but it's something to start with.
- VS2008 Express, Access, SQL Server 2005 Express, VB/C#/ADO.NET -
Rate posts that have been helpful to you! It's a way of giving back to someone who has helped you
Re: Threading and leaking memory - a demonstration
That looks right. Thanks for running the sample!
I thought the threads were supposed to die when they left the scope of their routine but this actually makes more sense as it is instead tied to the scope of the thread variable.
I won't have time to follow up on this until tomorrow PM. I'll let you know how it goes.
Re: Threading and leaking memory - a demonstration
Okay. This is not the answer and the problem looks to be much bigger then I first thought.
The current usage of "disposable" threads, which I thought was more or less how MS had intended it has some really ugly side effects besides just memory leaking.
First, the Abort does seem to help with the memory issue. There's a lot of jumping around in terms of memory utilization but that's likely attributable the "whenever I happen to get around to it" algorithm of the garbage collector. There may still be a slight accumulation but I haven't gotten that far.
What I have noticed is that closing the app does not kill the process. This is true both in and out of the debugger. Tried a finalize to specifically kill the threads (thinking their activity was blocking the process kill). No go. If you check the non-debug version in Task Manager, you'll see the app go away but the Process lingers on.
While threads/processes are always a bit dicey, I do think that the current scheme seems to be well short of ready for prime time. I may need to try Threadpooling even with it's increased overhead. The thought would be that thread reuse may allow for a more stable runtime than constant creation/destruction.
Re: Threading and leaking memory - a demonstration
It's not closing because you're still orphaning threads when you just close the form after starting your creation loop. Again, in the form closing event, cycle through all of your open process objects and call abort on their threads if they are not nothing. Then your application will close.
- VS2008 Express, Access, SQL Server 2005 Express, VB/C#/ADO.NET -
Rate posts that have been helpful to you! It's a way of giving back to someone who has helped you
Re: Threading and leaking memory - a demonstration
And just as a sort if whining theoretical question, from what I've read of threads wouldn't one expect .NET to clean up after itself? I know that's not real life or we'd all be "Drinkin' that free Bubble Up and eatin' that Rainbow Stew." I'm just thinking that the threading seems to be positioned like that in the .NET framework and it seems a good deal removed from that.
Re: Threading and leaking memory - a demonstration
Okay,
Here's something I'd discovered a time ago that dawned on me last night as I was falling asleep.
YOU CAN'T USE A FOR LOOP (IN VB) FOR THIS SORT OF ACTIVITY. If you use a do or a while with a Public control variable, THEN you use the relevant Form.closing event to set the control variable to done. That exits the loop and allows the app to die.
Re: Threading and leaking memory - a demonstration
This version has the .Abort commented out. I was experimenting.
Here's the class with the sub that hosts the loop:
Code:
Imports System.Threading
Public Class RunClass
Public Done As Boolean = False
Public ProcessObjectArray(NT_Test.NUM_THREADS) As ProcessClass
Public ThreadCollection As New Collection
'Dim t(NT_Test.NUM_THREADS) As Thread
Dim WithEvents PO As ProcessClass
Public x As Integer = 0
Public Shared LockObject As New Object
Public Sub RunIt()
'Dim t As Thread
'Dim i As Integer = 0
'Dim PO As New ProcessClass
'PO.sDisplayText = i.ToString
't = New Thread(AddressOf xyz)
't.Start()
Dim Start As Date = Now
ThreadCollection.Clear()
For i As Integer = 1 To NT_Test.NUM_THREADS
Dim POx As New ProcessClass
'Dim POz As New ProcessClass
POx.sDisplayText = i.ToString
ThreadCollection.Add(POx, i.ToString)
Next
Dim u As Integer = 0
While Not Done
Dim x As Integer = 0
Dim POy As New ProcessClass
System.Windows.Forms.Application.DoEvents()
For Each POy In ThreadCollection
'PO = POy.Clone
'If Not POy.t Is Nothing Then POy.t.Abort()
POy.t = New Thread(AddressOf POy.DoThreadWork)
POy.t.Start()
Next
'System.Windows.Forms.Application.DoEvents()
Dim POv As New ProcessClass
'For Each POv In ThreadCollection
'NT_Test.Label1.Text = POv.sDisplayText
'Next
For Each POv In ThreadCollection
Dim bREallydone As Boolean = True
bREallydone = POv.t.IsAlive
Next
NT_Test.Label1.Text = u.ToString
System.Windows.Forms.Application.DoEvents()
u = u + 1
If u = 10000 Then
Done = True
End If
End While
Dim Ending As Date = Now
Dim Difference As System.TimeSpan = Nothing
Difference = Ending - Start
End Sub
Agreed .Abort is nice if it can be avoided. The problem seems to be though that .NET does not behave as promised. That is, do what you like and the omniscient garbage collector will clean up after you. When that sort of thing doesn't work as advertised, then "jiggling the wires" unfortunately becomes necessary. I've never gotten through a large project without stumbling over something like this.
Last edited by jbmckim; Aug 11th, 2010 at 11:16 AM.
Re: Threading and leaking memory - a demonstration
This
Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
For x As Integer = 1 To 50
Dim t As New Threading.Thread(AddressOf foo)
t.Start()
Next
End Sub
Const aSZ As Integer = 1024 * 1024 - 1
Public Sub foo()
Dim a(aSZ) As Long
Do
Threading.Thread.Sleep(100)
Loop
End Sub
End Class
type of code will cause errors.
This is slightly better
Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
For x As Integer = 1 To 50
Dim t As New Threading.Thread(AddressOf foo)
t.IsBackground = True
t.Start()
Next
End Sub
Const aSZ As Integer = 1024 * 1024 - 1
Public Sub foo()
Dim a(aSZ) As Long
Do
Threading.Thread.Sleep(100)
Loop
End Sub
End Class
and instead of .Abort
Code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim l As Long = Threading.Interlocked.Exchange(isRun, 1L)
For x As Integer = 1 To 1
Dim t As New Threading.Thread(AddressOf foo)
t.IsBackground = True
t.Start()
Next
End Sub
Private isRun As Long = 0
Const aSZ As Integer = 1024 * 1024 - 1
Public Sub foo()
Dim a(aSZ) As Long
Do
Threading.Thread.Sleep(100)
Loop While Threading.Interlocked.Read(isRun) > 0L
a = Nothing
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Dim l As Long = Threading.Interlocked.Exchange(isRun, 0L)
End Sub
End Class
Re: Threading and leaking memory - a demonstration
"That is, do what you like and the omniscient garbage collector will clean up after you." -- I don't think that's ever really been true... It's gotten BETTER (compared to VB6) ... but it's still far from perfect, and still requires a fair amount of due diligence on the developer. In addition, I'd argue that what you are doing is probably not typical of a majority of applications out there, a vast majority of which the GC works "as advertised" and reasonably well.
I think the problem is that when it comes to threading, some normal rules get thrown out... making memory management a bit more difficult.
Just my two centavos.
Is there a reason for using "Collection" instead of List(Of T) or a Dictionary(Of K, T)? I wonder if using a Dictionary and making it private rather than Public would change anything. Just thinking out loud here.
Re: Threading and leaking memory - a demonstration
I'm going to fiddle with both the ideas in both of these posts.
My biggest concern here is with memory leaks. Running some endurance tests, it seems that .Abort does make the problem smaller/slower to manifest, but doesn't eliminate it.
I would think that, while the scale of what I'm doing here might be larger than most, that it would be a fairly typical use of threading. I've used process splitting and threading in tcp/ip apps (c++) a few times in this manner.
My point in Bill bashing is only to prod past the reflexive responses of "That's how it's supposed to work." It surely is a good design criteria to get rid of all the thread/process paraphernalia. The problem seems to be that some of that stuff is required to manage the stresses that threading (used in anything other than putting up a status bar for a background update) produces.
So...off to jiggle more wires.
I'm going to also experiment with thread pooling to see if that mechanism handles this sort of stressing any more elegantly. I will keep any interested soul informed.
Re: Threading and leaking memory - a demonstration
I wasn't suggesting that the answer was "that's how it's supposed to work" ... my point is that, as is, it is a 90% solution. I'm sorry you fall into that other 10%. I've been caught in that 10% a couple times myself. It's a fact of life sometimes. Personally, I'd much rather see a more aggressive GC, or some way of dealing with it myself. I'd like to be able to do something like this: object.Release .... it would dispose it, mark it for clean up AND release the memory all in one shot.
Re: Threading and leaking memory - a demonstration
Exactly TG,
You can never trust email to fully carry the meaning (or to not carry the wrong meaning), so to the extent I came across like I was trying to assign that to your motivation, I apologize. That wasn't intended.
The "bleeding edge" is where I've had the "opportunity" to spend most of my career. Usually, when you try to talk to 90% of folk about your specific challenge, they respond with any number of 101 level answers on the subject. That's natural, because they project their own experience onto yours. (I actually do this in reverse to the point where I confuse and occasionally frustrate people.) AND multiply all that by the confusion that comes from doing it in writing as opposed to over coffee (or tequila) and it's a wonder anything useful happens at all.
I will say though that the help on this board is consistently good. I am making progress. The advice given to this point gives good direction. I haven't yet explored List or Dictionary and I do plan to mess with that some as well.
Finally, your point as regards the desired .Dispose is very good. That would be resource intensive but very desirable for the type of thing I'm dealing with. Let's hope MS is listening...to someone...somewhere.
Re: Threading and leaking memory - a demonstration
dbasnett: More good info. Suggests that I'll experiment with moving the abort to the thread and having it call it on itself as the last thing it does before losing scope.
NOTE TO ALL: Running this in the debugger is slower than dirt. However, running the .exe is completely acceptable. This is the greatest delta I've seen to date in terms of debug environment vs. .exe. If you're travelling this path, don't jump to performance conclusions based on debugger performance.
Re: Threading and leaking memory - a demonstration
jbm - try the tequila in the coffee....
As a case in point regarding how importaint it is to call .Dispose... I actually came across documentation in MSDN on a class where it stated that because the object has no Finalize method... it was IMPERATIVE that the developer call the .Displose method, because doing so would allow the class to properly disconnect from the remote server and close properly (it was the SMTPClient class... in order to diconnect, it needs to sent a QUIT command to the SMTP server to close the connection... which only happens in the Dispose event. !!! )
Re: Threading and leaking memory - a demonstration
techgnome - thanks for the recipe. If we ever run into each other, I'll have one with you.
I just wanted to update anyone who's interested with what I've learned thus far...and to me anyway it's pretty interesting.
Although it seems to be a popular idea, the System.Threading.Thread approach, per:
Code:
Public t As Thread
t = New Thread(AddressOf POy.DoThreadWork)
t.Start()
seems to be pretty resource intensive. This makes sense I think because the idea here is that you're building, using and then throwing things away as you go. Even on current cpu's this can get expensive quickly.
For high volume threading, I'm finding that Thread pooling is extraordinarily faster. However, there's a catch.
It's so fast, that if you have a large number of threads that iterate repeatedly (think looping through an array - or like - of running threads over and over), it looks like whatever-they-call-the-time-slice-chipper-on-Vista/7, begins to have to queue up entries into the return from the thread. Essentially, you start to create a rush-hour style blockage at the point that the threads are being created/initiated just because the threads themselves are so damn fast.
I'm currently experimenting with system sleeps in both the mainline and the threads to see how the cpu/memory managers respond. As you can likely tell, I'm not yet understanding what I'm seeing very well but the entire environment is leaking less, using much less cpu AND completing in slightly better elapsed time than was previously the case. (Again, pooling is being used as well, so there's multiple controls in play.)
For the time being, I'd say pretty certainly that if you have a really intense throughput requirement, you go directly to the threadpool and stay away from dynamic thread creation/destruction.
I'll post my new code when I understand what it's actually telling me a little better.
Re: Threading and leaking memory - a demonstration
I may be missing something, here, but isn't the result of this whole exercise a case of 'you don't get something for nothing'?
If you are running lots of very short lived tasks, why create a thread for each task? Why not create a single thread and queue those tasks?
It seems like you are making a shopping list and driving to the store, getting a single item then driving home; getting back in the car to get the next item on the list, driving to the store, and so on. As you normally do, you get all the items in one trip.
The thread pool, however, is designed to do that: the ability to create threads very quickly on demand. But even that takes resources - that's why servers tend to be multi-processor with a huge dollop of RAM.
While the 'standard' philosophy is to create and access resources when you need them, if the act of simply creating the resource and it's requirements is expensive, then creating it ahead of time can be beneficial.
You could create a single thread which accesses a queue of objects to work on. If there's no objects then it sits there, idling in a do/loop or some such. Your main thread (or threads) then put these objects in the queue to be worked on, the worker thread signaling when each item is complete.
Also, I don't think there is a memory leak as such: yes, memory appears to be consumed, but the framework is not throwing things away because you keep needing the memory very, very quickly. Disposing the objects can be expensive also, so rather than kill the application performance to take a timeout to clean up, it just assigns it more memory.
In other words, considering a 'shopping list' example, rather than waiting for the first car to come back from the store, a new car is used to get the second item, and another for the third and so on. Since each car can be only used once (it's a thread that has completed), you can either take time out to sell the car or get another car for the next item on the list. To maintain performance (getting items on the list) the junk heap of cars is going to get pretty big before there's time to dispose of them.
"Ok, my response to that is pending a Google search" - Bucky Katt. "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk. "Before you can 'think outside the box' you need to understand where the box is."
Re: Threading and leaking memory - a demonstration
SJ - If I read you correctly, we find ourselves mostly in violent agreement.
You could create a single thread which accesses a queue of objects to work on.
Again, if I'm understanding you, I don't think this works in the context of my requirement. I'm reading a number of USB devices. Each device incurs relatively substantial latency (20-100 milliseconds). The point is to incur the collective latency at one time, such that I'm reading 30 devices more or less at once rather than sequentially.
Or using your example, I have a LOT of groceries to get. I'd rather send 30 cars and drivers to the store at once and get them back home so we can fire up the party sooner.
Since building a threadpool based testbed (as opposed to the example I originally uploaded), it seems that stripping away the overhead of thread construction/disposal gives a clearer view of how resources are utilized. I think what I'm now seeing is pretty much in line with what you describe. The cpu consumption increases gradually over the life of the run. This is expected in any reasonably tight loop, being run under any conventional sort of instruction queuing scheme. I'm still seeing a good bit of memory loss though. I have however, noticed a correlation between a delay posted (System Sleep) in the Run Class corresponding to a lessening of memory consumption. I'm going to experiment with this some more to see where it leads.
I don't think its a case of wanting something for nothing. I think it's more about reading the MS documentation (which sometimes is a bit close to marketing literature) and then wanting to understand how MS is managing time division queuing given threading, viz a vi my requirement. I do wish this implementation offered a few more tools for managing this process and maybe inquiring into it at run time. Due to non-technical constraints, I'm bounded to a .NET implementation. I'd have much rather gone with c++ in an unmanaged solution for this sort of thing.
Re: Threading and leaking memory - a demonstration
Originally Posted by jbmckim
SJ - If I read you correctly, we find ourselves mostly in violent agreement.
Again, if I'm understanding you, I don't think this works in the context of my requirement. I'm reading a number of USB devices. Each device incurs relatively substantial latency (20-100 milliseconds). The point is to incur the collective latency at one time, such that I'm reading 30 devices more or less at once rather than sequentially.
Or using your example, I have a LOT of groceries to get. I'd rather send 30 cars and drivers to the store at once and get them back home so we can fire up the party sooner.
Since building a threadpool based testbed (as opposed to the example I originally uploaded), it seems that stripping away the overhead of thread construction/disposal gives a clearer view of how resources are utilized. I think what I'm now seeing is pretty much in line with what you describe. The cpu consumption increases gradually over the life of the run. This is expected in any reasonably tight loop, being run under any conventional sort of instruction queuing scheme. I'm still seeing a good bit of memory loss though. I have however, noticed a correlation between a delay posted (System Sleep) in the Run Class corresponding to a lessening of memory consumption. I'm going to experiment with this some more to see where it leads.
I don't think its a case of wanting something for nothing. I think it's more about reading the MS documentation (which sometimes is a bit close to marketing literature) and then wanting to understand how MS is managing time division queuing given threading, viz a vi my requirement. I do wish this implementation offered a few more tools for managing this process and maybe inquiring into it at run time. Due to non-technical constraints, I'm bounded to a .NET implementation. I'd have much rather gone with c++ in an unmanaged solution for this sort of thing.
I guess I missed where you told us this was about reading from USB devices. Why not start permanent threads for each device that reads the data and places the data into a queue that processes it from another thread / UI thread. I did something similar using two USB to serial port adapters. It would help to know much more about your USB devices.
Last edited by dbasnett; Aug 13th, 2010 at 11:30 AM.
Re: Threading and leaking memory - a demonstration
It is my humble opinion that if Bill Gates had been a systems programmer on a large mainframe, with multiple processors and lots of IO devices(multiple strings of disk drives, tape drives, card readers, card punches, line printers, terminals, etc.) before the career we all know of, threading would be a lot easier. Multi-programming / multi-processing are not difficult concepts, though they are less forgiving.
Re: Threading and leaking memory - a demonstration
@dbasnet -
I'll answer your 2nd post first: AMEN BROTHER! BITD, this sort of thing was easy on HP and DEC iron and Bill was nowhere to be seen.
First post: Don't feel bad, you missed the USB part because I didn't include it. I was mainly concerning myself with .NET thread design behavior. Didn't imagine anyone would be interested (and they might even be put off) by such a low level description.
Essentially though, I'm dealing with an array of broadband optical spectrometers, supporting a mfg process. These are fun little devices with reasonably solid firmware...although I do have to climb down on the eprom from time to time for some quality burn time. The real time requirement is pretty lax, best expressed by something like: "As fast as you possibly can." There are no tick or time coordination requirements.
The spectrometers have two settings that drastically affect latency. 1) integration time (same thing as length of exposure on your camera) and scans to average (this is a way of smoothing the wave produced by the spectrometer reading). Both are expressed in milliseconds and the two are multiplied by each other to get total et for the measurement. So Integration * Scans To Average = Elapsed time. (You can start to see why I want to thread this - Threading allows me to incur the ET something like simultaneously.)
The software I'm writing scales to the number of spectrometers the customer wants in place during his mfg process (Vacuum deposition). The usual number is somewhere between 1 and 8...maybe 12 on a huge system. Single threading on a 4 core or better cpu, generally handles these requirements within specification pretty handily. So, naturally, we have someone standing in the door holding a requirement for 72 (each spectrometer will support 2 points of measurement via an optical switch) in one hand and a stinking obscene pile of money in the other hand...we're trying really hard to get them to actually come inside the door.
Okay, as I said above, the app scales. A database set up at our build time informs the software how many spectrometers it's going to be managing. At run time, the software reads the db and builds a discrete object for each spectrometer. Each spec object manages measurement, display and interface to a separate USB based switch
Each spec object, is "run" from an external program. This is mimicked by the project file I posted...except for the fact that the spectrometer class/objects contain about 3000 lines of code (I'm not real proud of that).
Do you have a pattern or know where I can find one for establishing permanent threads in .NET? This wasn't clear to me from the stuff I looked at. The threadpool looked to be closed to this paradigm.
Thanks & I hope you're not now BOTH blind and bored.
Re: Threading and leaking memory - a demonstration
I'd use a single thread per device; if you have 64 devices, that's 64 threads. Add a device, add a thread. Remove a device, remove a thread.
Essentially, you'll have the thread created when your device is detected and connected (presumably through your high level 'device manager' class). That thread will essentially have a Do/Loop in it managing the device (the thread will be encapsulated in a class, of course).
Now, I'm not certain of the details about how the data is managed and acquired from the device, but it's possible you may need an additional thread per device; what you will be doing is using a single thread to manage data acquisition as fast as possible, the original device thread treating/massaging the data and presenting it to the manager. I think that's a worst-case scenario, though.
Essentially, each thread is created once: you won't kill the threads except when the device is removed or the application shuts down.
The thread pool isn't appropriate for your application, I think. That's designed for something like a web server where the application doesn't know what the requirements may be at any given time. Your app is predictable: you have a given number of devices that exist for the life of the application (give or take).
"Ok, my response to that is pending a Google search" - Bucky Katt. "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk. "Before you can 'think outside the box' you need to understand where the box is."
Re: Threading and leaking memory - a demonstration
That would be ideal. Are you aware of any decent patterns for persistent threads in VB? I have a couple books that deal with the pool and Background workers, one that treats different forms of threading but nothing mentions persistent threading.
The concern I have is that a good many things in VB.NET are meant to disappear when they "go out of scope." If persistent threading is derived from the elements I've seen so far, my concern is that I'll incur unintended deletion and recreation.
I can of course, build by own "non volatile" thread array/list/collection/etc and create what is essentially a custom threadpool of my own.
Re: Threading and leaking memory - a demonstration
Quick update on my way to completing this. Folding,spindling and mutilating the example code posted by dbasnett to more closely resemble my multi-class requirement, the memory and cpu stability seem pretty solid thus far. There's a lot of fluctuation but the GC eventually catches up and returns to what looks to be a baseline range. Further, introducing a sleep into the outermost loop seems to drop memory fluctuation to 0 - I suspect the GC is doing it's thing in the time between loops.
I've got a bit more to investigate, however no questions for now. When done, I'll rate the posts of all and upload my finished test bed in case anyone is REALLY bored and wants to investigate further.
Re: Threading and leaking memory - a demonstration
Got pulled off for a few days onto something else...sorry for the delay.
The "Interlock" method above works fairly well but is not determinate. Thread events can execute unexpectedly and occasionally miss an interlock toggle. The degree of leakage corresponds to the number of threads executed. Fewer threads = more leakage. This makes sense if you step back from the PC, go for a walk and think a bit.
Looked further and I seem to have found what I was originally looking for in the context of my design "challenge." Essentially this is a "Wait" implementation in which threads can be idle until their respective waits are unblocked, AND the main thread is idle until the "server" threads complete. (All the usual disclaimers about coordination/timeouts and deadlocks apply.)
There's a problem though. Using the code/project below:
Imports System.Threading
Public Class Form1
Public WaitThreadEvents(2) As AutoResetEvent
<MTAThread()> _
Sub Main()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For i As Integer = 0 To 2
WaitThreadEvents(i) = New AutoResetEvent(False)
Next
WaitHandle.WaitAll(WaitThreadEvents)
End Sub
End Class
WaitHandle throws the exception: WaitAll for multiple handles on a STA thread is not supported.
I've explicitly declared the MTA Apartment structure, so what's up?
Thanks.
PS: Also tried using MTAThreadAttribute with same result.