I have created a test application to test my multithreading by creating threads which put the numbers 1 to 100 in columns of an excel spreadsheet. I use a form1 button click to start the threads. One thread starts and then, after 2 seconds the next thread starts so I can see all the threads running at the same time
Code:
01 Public Class Form1
02
03 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
04 Dim testthread As New System.Threading.Thread(AddressOf Module1.loop3)
05 Dim testthread1 As New System.Threading.Thread(AddressOf Module1.loop3)
06 Dim testthread2 As New System.Threading.Thread(AddressOf Module1.loop3)
07 Dim testthread3 As New System.Threading.Thread(AddressOf Module1.loop3)
08 Dim testthread4 As New System.Threading.Thread(AddressOf Module1.loop3)
09 testthread.Start(1)
10 System.Threading.Thread.Sleep(2000)
11
12 testthread1.Start(2)
13 System.Threading.Thread.Sleep(2000)
14 testthread2.Start(3)
15 System.Threading.Thread.Sleep(2000)
16 testthread3.Start(4)
17 System.Threading.Thread.Sleep(2000)
18 testthread4.Start(5)
19 System.Threading.Thread.Sleep(2000)
20 Call Module1.loop3(6)
21 End Sub
22 End Class
01 Public Module Module1
02 Dim xl = CreateObject("excel.application")
03 Delegate Sub testo()
04
05
06
07
08 Public Sub loop3(ByVal column As Integer)
09
13 If xl.visible <> True Then
14 xl.visible = True
15 End If
19 Try
20 If Not xl.activeworkbook.name = "Spenddown IN Mdcd.xlsm" Then
21 xl.workbooks.open("C:\Documents and Settings\BMcGuir1\Desktop\Test\spenddown in mdcd.xlsm")
22 GoTo done
23 End If
24
25 Catch
26 xl.workbooks.open("C:\Documents and Settings\BMcGuir1\Desktop\Test\spenddown in mdcd.xlsm")
27 End Try
28
29 done:
36 Dim y As Long
37 For y = 1 To 100
38 xl.activeworkbook.sheets("sheet1").cells(y, column).value = y
39 System.Threading.Thread.Sleep(100)
40 Next
41 'MsgBox("done")
42 End Sub
43
44 End Module
I have this EXACT same code on form2 on another project and the sleeps between the thread starts don't work. When I run it on another project all 6 threads start after the buttonclick event is over. when 6 threads try to open an excel spreadsheet at the same time problems start. Why is sleep() working on one application but not another? Any help is appreciated. I tried to attach the project in which sleep is not working but the server returned an error during upload. I've attached the application to this post. I've also researched to try to find out why sleep() would not work and I have not found anything.
Hi, if it works in one project and not another then the problem would lie in code somewhere. Have you imported the system.threading? also wouldnt you need to tell it which thread to sleep? as in
Hi, if it works in one project and not another then the problem would lie in code somewhere. Have you imported the system.threading? also wouldnt you need to tell it which thread to sleep? as in
Code:
testthread2.Sleep(2000)
i'm not wanting to sleep testthread2. I'm wanting to start a thread, sleep for 2 seconds, start a thread, sleep for 2 seconds, start a thread. I attached the project to my post so you can see the code.
If this is just an exercise in testing out Sleep, why deal with Excel at all? If this isn't just an exercise in testing sleep....well, then there are plenty of oddities to that code. The most basic question, though, is this: How do you know that it is not sleeping? You just say that all six threads start after the click event. That doesn't sound like the sleep isn't happening, that sounds like you are expecting something to happen with the threads IMMEDIATELY, before the sleep happens. There's no reason to expect that. All those threads work on their own speed, and that will be dependent on other factors that you have little or no control over.
Frankly, if you are having trouble with working with Excel in multiple threads, then why are you working with Excel in multiple threads in the first place? The design makes no sense, as written, but it certainly sounds like it is working based on what description you gave.
If this is just an exercise in testing out Sleep, why deal with Excel at all? If this isn't just an exercise in testing sleep....well, then there are plenty of oddities to that code. The most basic question, though, is this: How do you know that it is not sleeping? You just say that all six threads start after the click event. That doesn't sound like the sleep isn't happening, that sounds like you are expecting something to happen with the threads IMMEDIATELY, before the sleep happens. There's no reason to expect that. All those threads work on their own speed, and that will be dependent on other factors that you have little or no control over.
Frankly, if you are having trouble with working with Excel in multiple threads, then why are you working with Excel in multiple threads in the first place? The design makes no sense, as written, but it certainly sounds like it is working based on what description you gave.
My questions is this:
When I run this code on this application that i've attached:
All 6 threads start at the exact same time.
When I run this code on another application:
Thread one starts, and then after 2 seconds thread 2 starts, then after two seconds thread 3 starts, then after two seconds thread 4 starts.
How come, on this application, all the threads start at the same time while on another application the sleep() causes there to be a 2 second delay between threads starting? I know that it's not sleeping because when I run this EXACT code on another application the sleep(2000) is causing there to be a 2 second delay in the thread starts
How do you know when the thread starts? What means are you using to determine that?
EDIT: The reason I ask this is that you seem to be wanting multi-threading to act in a way that it does not. Once you start a thread, you have no control over WHEN it gets to ANY particular statement. That comes down to how the OS deals with threads, and it can vary in all kinds of different ways. You are starting a thread, then sleeping the main thread. If you could say "This other thread will accomplish X amount of work during these two seconds." then you would be asserting how far a thread must get during some segment of time, which you can't safely do under any circumstance. Therefore, even wanting this is dangerous, because you might come to rely on a measure you can't guarantee. The very same code that worked on one system at one time might not work on a different system, or the same system at a different time. You'd have a race condition, which is the nastiest possible bug because failure would be unpredictable and intermittent.
Last edited by Shaggy Hiker; Jan 30th, 2013 at 11:27 AM.
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim stpw As New Stopwatch
stpw.Restart()
Threading.Thread.Sleep(2000)
stpw.Stop()
Debug.WriteLine(stpw.Elapsed.ToString)
stpw.Restart()
Threading.Thread.Sleep(2000)
stpw.Stop()
Debug.WriteLine(stpw.Elapsed.ToString)
stpw.Restart()
Threading.Thread.Sleep(2000)
stpw.Stop()
Debug.WriteLine(stpw.Elapsed.ToString)
End Sub
How do you know when the thread starts? What means are you using to determine that?
EDIT: The reason I ask this is that you seem to be wanting multi-threading to act in a way that it does not. Once you start a thread, you have no control over WHEN it gets to ANY particular statement. That comes down to how the OS deals with threads, and it can vary in all kinds of different ways. You are starting a thread, then sleeping the main thread. If you could say "This other thread will accomplish X amount of work during these two seconds." then you would be asserting how far a thread must get during some segment of time, which you can't safely do under any circumstance. Therefore, even wanting this is dangerous, because you might come to rely on a measure you can't guarantee. The very same code that worked on one system at one time might not work on a different system, or the same system at a different time. You'd have a race condition, which is the nastiest possible bug because failure would be unpredictable and intermittent.
I know when the thread starts because I can see the numbers being put on the column of an excel spreadsheet. Thread 1 is column 1, thread 2 is column 2, thread 3 is columng 3 etc.
I run this on one app and I see the spreadsheet and I can visibily see thread 1 start putting numbers on column 1 and then, after 2 seconds, thread 2 start putting numbers into column 2 and then, after 2 seconds thread 3 starts putting numbers into column 3.
I run this on the app that i've linked here and when I press the button all 6 columns are getting numbers at the same time. There is no delay between one column and another
[QUOTE=dbasnett;4332341]So I did a test. In form1 I have this
Code:
Dim f As New Form2
f.Show()
how would it affect my code if I just had form2.show() instead of dimming f as new form2 and f.show()? On my code I just have form2.show() although i tried showing my form2 like this and sleep() still didn't sleep.
Last edited by iamcpc; Jan 30th, 2013 at 12:39 PM.
That could be the issue, or part of it. You are using the default instance. Normally, objects are not thread specific, but default instances work in a different fashion that can have some suprising implications. It doesn't seem to me that the use of the default instance should cause the behavior you are seeing, but what you have described is some entanglements of one sort or another. I wouldn't expect that the thread affinity of the default instance would translate into thread affinity for the other threads, but I don't know how MS implemented the thing.
The easiest solution, since it is a good idea in pretty nearly EVERY case, is to try not using the default instance. The reason I dislike the default instance isn't because of thread affinity, but because it causes horrendous confusion without providing any real benefits to most people.
That could be the issue, or part of it. You are using the default instance. Normally, objects are not thread specific, but default instances work in a different fashion that can have some suprising implications. It doesn't seem to me that the use of the default instance should cause the behavior you are seeing, but what you have described is some entanglements of one sort or another. I wouldn't expect that the thread affinity of the default instance would translate into thread affinity for the other threads, but I don't know how MS implemented the thing.
The easiest solution, since it is a good idea in pretty nearly EVERY case, is to try not using the default instance. The reason I dislike the default instance isn't because of thread affinity, but because it causes horrendous confusion without providing any real benefits to most people.
I tried dim frm2 as new form2 and I also tried to make dim f2 as new form2 outside of the sub as a class level variable and they both still had the same problem
That's good, frankly. I was having a hard time figuring out how the thread affinity of the default instance could impact threads spawned by the object.
So, is this a correct explanation of what you expect:
Numbers come from thread 1.
Wait 2 seconds.
Numbers come from thread 2.
Wait 2 seconds.
Numbers come from thread 3.
And what you are actually seeing is:
Wait 6 seconds.
Numbers come from threads 1, 2, and 3
Or is what you are seeing more like this:
Wait 6 seconds
Numbers come from thread 1
Numbers come from thread 2
Numbers come from thread 3
If either of those two are the way you are seeing this behave, then the first thing I would do would be to extend the wait time from 2 seconds to 10 seconds. If the delay then went from 6 seconds to 30 seconds (a difference that would be VERY obvious, which was all I was looking for), then that would suggest one issue, but if the delay didn't go to 30 seconds, that would suggest a different issue. Aside from that, the other question is whether the three threads, when they do run, run simultaneously or run sequentially?
I also noticed that you are using DoEvents, though I don't think it is the problem, but it is something to look at. I have never had or seen an actual failure of sleeping a thread.
That's good, frankly. I was having a hard time figuring out how the thread affinity of the default instance could impact threads spawned by the object.
So, is this a correct explanation of what you expect:
Numbers come from thread 1.
Wait 2 seconds.
Numbers come from thread 2.
Wait 2 seconds.
Numbers come from thread 3.
And what you are actually seeing is:
Wait 6 seconds.
Numbers come from threads 1, 2, and 3
This is what I'm seeing
If either of those two are the way you are seeing this behave, then the first thing I would do would be to extend the wait time from 2 seconds to 10 seconds. If the delay then went from 6 seconds to 30 seconds (a difference that would be VERY obvious, which was all I was looking for), then that would suggest one issue, but if the delay didn't go to 30 seconds, that would suggest a different issue. Aside from that, the other question is whether the three threads, when they do run, run simultaneously or run sequentially?
If I try this
Numbers come from thread 1.
Wait 20 seconds.
Numbers come from thread 2.
Wait 20 seconds.
Numbers come from thread 3.
wait 20 seconds.
I wait 60 seconds and then everything starts at once whereas I do that same thing on another app and the sleeps actually sleep.
Ok, that's good information. The three threads are being queued up, but are not actually starting until after the UI thread is done sleeping. So, something blocks execution until released by the UI thread. Once that block is removed, all three threads run simultaneously, which means that the results will come out in any order. Had they been sequential, then there would have been some other kind of block that was allowing each thread to own a region in turn. The situation you have found is simpler, but it still isn't easy to solve.
What is causing the block? That I can't say. There is something different between the app that works and the app that doesn't work, and this difference could be nearly anything. For one thing, I have been assuming that both apps are on the same computer, so you aren't seeing a hardware difference. That leaves the module, itself, as the most likely culprit. After all, the threads all work with some non-thread local variables, such as the Excel object. From your description, the UI thread is locking on the object, then releasing the lock at the end of the button sub. Why it would do that, I can't say based on what you are showing, and the lock could actually be elsewhere. However, a few things can be tested:
Put breakpoints on these two lines:
In the module:
If xl.visible <> True Then
In the click event:
End Sub
Which breakpoint is hit first? If you get to the module breakpoint right away, then the Excel object is where the lock is being held, and that may well be something internal to Excel. If you get to the End Sub breakpoint first, then the Start call on the threads is not occuring until after the click event completes. That would be rather odd, because it's a bit hard to see where the block would be held that would prevent the setting up of the threads themselves. The threads are probably being pooled, so it would be acting like the thread scheduler was held by the UI thread for the duration of the button click. That would be rather odd.
The ultimate key is to figure out what the difference is between the app where it works and the app where it does not. The test I proposed would really do no more than narrow down what to look at. If the code you posted is complete for those methods, then the problem is occuring in code that you didn't show. Are you using threading elsewhere? Are you locking any objects explicitly?
Ok, that's good information. The three threads are being queued up, but are not actually starting until after the UI thread is done sleeping. So, something blocks execution until released by the UI thread. Once that block is removed, all three threads run simultaneously, which means that the results will come out in any order. Had they been sequential, then there would have been some other kind of block that was allowing each thread to own a region in turn. The situation you have found is simpler, but it still isn't easy to solve.
What is causing the block? That I can't say. There is something different between the app that works and the app that doesn't work, and this difference could be nearly anything. For one thing, I have been assuming that both apps are on the same computer, so you aren't seeing a hardware difference. That leaves the module, itself, as the most likely culprit. After all, the threads all work with some non-thread local variables, such as the Excel object. From your description, the UI thread is locking on the object, then releasing the lock at the end of the button sub. Why it would do that, I can't say based on what you are showing, and the lock could actually be elsewhere. However, a few things can be tested:
Put breakpoints on these two lines:
In the module:
If xl.visible <> True Then
In the click event:
End Sub
Which breakpoint is hit first? If you get to the module breakpoint right away, then the Excel object is where the lock is being held, and that may well be something internal to Excel. If you get to the End Sub breakpoint first, then the Start call on the threads is not occuring until after the click event completes. That would be rather odd, because it's a bit hard to see where the block would be held that would prevent the setting up of the threads themselves. The threads are probably being pooled, so it would be acting like the thread scheduler was held by the UI thread for the duration of the button click. That would be rather odd.
The ultimate key is to figure out what the difference is between the app where it works and the app where it does not. The test I proposed would really do no more than narrow down what to look at. If the code you posted is complete for those methods, then the problem is occuring in code that you didn't show. Are you using threading elsewhere? Are you locking any objects explicitly?
The If xl.visible <> True breakpoint is hitting first.
I'm not locking objects on either application. On the application where sleep() is not working on form1 I have Inherits System.Windows.Forms.Form. If I remove that inherits it has no effect on my sleep() problem.
You may not be locking, but SOMETHING sure is. If that breakpoint is hitting first then the threads actually ARE starting as you expect. The sleep is happening just as you would expect it to. The issue is that the output is not coming out as you expected, and that may be quite a bit harder to diagnose.
I'm not sure that it would be useful, but my next step would be to move the breakpoint further and further down that method to see how far into the method the thread gets before it blocks. I would guess that it blocks immediately, and that if you were to move the breakpoint down to this line:
If Not xl.activeworkbook.name = "Spenddown IN Mdcd.xlsm" Then
it would finish the button click (and hit the End Sub breakpoint) before hitting the breakpoint in the method. That would mean that the Excel app itself has been locked by the UI thread prior to any of the threads starting. However, it may be that you can move the breakpoint as far as this line:
xl.activeworkbook.sheets("sheet1").cells(y, column).value = y
in which case it is probably the workbook itself that is restricting access. Figuring that out may not help any, though.
That Inherits line isn't necessary. If it was necessary, the program wouldn't even have compiled when you removed it, because the app would no longer recognize ANY forms. Since removing it didn't stop the program from running entirely, then it isn't needed because the objects named are otherwise available.
You may not be locking, but SOMETHING sure is. If that breakpoint is hitting first then the threads actually ARE starting as you expect. The sleep is happening just as you would expect it to. The issue is that the output is not coming out as you expected, and that may be quite a bit harder to diagnose.
I'm not sure that it would be useful, but my next step would be to move the breakpoint further and further down that method to see how far into the method the thread gets before it blocks. I would guess that it blocks immediately, and that if you were to move the breakpoint down to this line:
If Not xl.activeworkbook.name = "Spenddown IN Mdcd.xlsm" Then
it would finish the button click (and hit the End Sub breakpoint) before hitting the breakpoint in the method. That would mean that the Excel app itself has been locked by the UI thread prior to any of the threads starting. However, it may be that you can move the breakpoint as far as this line:
xl.activeworkbook.sheets("sheet1").cells(y, column).value = y
in which case it is probably the workbook itself that is restricting access. Figuring that out may not help any, though.
That Inherits line isn't necessary. If it was necessary, the program wouldn't even have compiled when you removed it, because the app would no longer recognize ANY forms. Since removing it didn't stop the program from running entirely, then it isn't needed because the objects named are otherwise available.
When I move the breakpoint after making xl.visible I can see that the excel workbook is being made visible before the button click event ends.
I added a debug.print of the column variable which is being passed when the thread starts before it hits the breakpoint and thread 1 starts first, thread 6 starts second and thread 3 starts third. If there is a sleep(10,000) between starting the threads then shouldn't thread 1 hit first and thread 2 hit second?
I added debug prints to show what thread hits what part of the loop first. Why is it that the first thread is getting past making excel visible 50 seconds after the 5th thread is?
before visible 1
before visible 2
before visible 3
before visible 4
before visible 5
before visible 6
after visible 5
after visible 6
after visible 1
after visible 3
after visible 2
after visible 4
When all the threads are putting information on the spreadsheet at the same time I get and error "The object invoked has disconnected from its clients"
When I run the same thing on another app this is what happens:
before visible 1
after visible 1
after open 1
before visible 2
after visible 2
after open 2
before visible 3
after visible 3
after open 3
before visible 4
after visible 4
after open 4
before visible 5
after visible 5
after open 5
before visible 6
after visible 6
after open 6
Is there some setting or something on one app which is causing the threads to go out of order?
Last edited by iamcpc; Jan 30th, 2013 at 03:55 PM.
There is no guarantee of the order in the threads. That's entirely up to whim, as far as you are concerned. There are lots of factors that are involved, and you may well be able to identify some of them, but ultimately: The order of execution of threads can't be definitively determined.
That may be the whole issue. Why it's happening right on one and not on the other may be pure coincidence. The one thing you can say for certain is this: If you care about the order of execution of threads, then you have to force them to that order (generally by not using threads). You can't ever count on threads to run in any particular order or else you are just asking for trouble. Having the proper execution of an app depend on the order of execution of a series of concurrent threads is a race condition, which is pretty much always a bug. Thread scheduling is up to the OS. There are rules as to how threads will be added, how they will get time allocated to them, and so on. You can't change that in any good way (you can change it in a really BAD way by fiddling with priority levels, but don't do that).
Having said that, due to the amount of time that you are sleeping the threads, the pattern you are seeing is probably due to things other than race conditions, alone. There is a chokepoint somewhere in the code that is controlling passage for some critical section of the code. This could be in Excel, or it could be elsewhere. You may or may not be able to figure it out, but even if you did figure it out you would be well advised not to really rely on the order of execution for threads. If the order is necessary, get rid of the threads and make them operate in the order you need.
There is no guarantee of the order in the threads. That's entirely up to whim, as far as you are concerned. There are lots of factors that are involved, and you may well be able to identify some of them, but ultimately: The order of execution of threads can't be definitively determined.
That may be the whole issue. Why it's happening right on one and not on the other may be pure coincidence. The one thing you can say for certain is this: If you care about the order of execution of threads, then you have to force them to that order (generally by not using threads). You can't ever count on threads to run in any particular order or else you are just asking for trouble. Having the proper execution of an app depend on the order of execution of a series of concurrent threads is a race condition, which is pretty much always a bug. Thread scheduling is up to the OS. There are rules as to how threads will be added, how they will get time allocated to them, and so on. You can't change that in any good way (you can change it in a really BAD way by fiddling with priority levels, but don't do that).
Having said that, due to the amount of time that you are sleeping the threads, the pattern you are seeing is probably due to things other than race conditions, alone. There is a chokepoint somewhere in the code that is controlling passage for some critical section of the code. This could be in Excel, or it could be elsewhere. You may or may not be able to figure it out, but even if you did figure it out you would be well advised not to really rely on the order of execution for threads. If the order is necessary, get rid of the threads and make them operate in the order you need.
how can I do multiple things at the simeltaneously (like filling in columns on a spreadsheet) if i'm not using multiple threads? If the order of execution of threads can't be determined then how come i'm able to determine the execution of threads when I run this same code on another application. The thread that I start first executes first and the thread that I start second executes second etc...
Also if I start a thread that takes 10 seconds to run and then sleep() after starting that thread for 20 seconds shouldn't that thread be complete after the sleep? Why is it on here that when I sleep for 20 seconds after starting a 10 second thread and the 10 second thread has yet to anything other than a debug.print after 20 seconds?
Last edited by iamcpc; Jan 30th, 2013 at 05:14 PM.
In what world do spreadsheet columns get filled in simultaneously? A better way to ensure that the spreadsheet program crashes I cannot imagine!
As has been stated, you are not in control of the execution order of multiple threads. Even if everything works the way you want it to you only think that you are in control because of the coincidence. Once you have it firmly established that you are not in control your final question answers itself. You are not in control. You cannot get control. Multiple threads will ultimately do whatever they like!
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
In what world do spreadsheet columns get filled in simultaneously? A better way to ensure that the spreadsheet program crashes I cannot imagine!
I have posted the code in which spreadsheet columns are filled simultaneously without crashing the spreadsheet program on this thread.
Originally Posted by dunfiddlin
As has been stated, you are not in control of the execution order of multiple threads. Even if everything works the way you want it to you only think that you are in control because of the coincidence. Once you have it firmly established that you are not in control your final question answers itself. You are not in control. You cannot get control. Multiple threads will ultimately do whatever they like!
If that is true then why is it, using the code posted on this forum in a different application, I can start a 10 second thread and sleep() for 20 seconds and see that the 10 second thread I just started is done after 10 seconds during the sleep()?
Last edited by iamcpc; Jan 30th, 2013 at 09:18 PM.
I also agree with the general idea that if order or execution is essential to your purpose then you really should not be using threads.
This forum post is not about controlling the order of execution of my threads. It's about understanding why sleep() works fine with the same code, same computer, different application then on this appliction appears to be not working.
Thread execution order is inherently unpredictable which is about the best answer for your question. Your observation about threads all executing after all the sleeps is extreme but not surprising to me given the way threads work in Windows.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Thread execution order is inherently unpredictable which is about the best answer for your question. Your observation about threads all executing after all the sleeps is extreme but not surprising to me given the way threads work in Windows.
Actually you have your answer. There is no guarantee that when you call a Thread object's Start method that the thread would begin immediately. That's what everyone including me is saying. That thread will start anytime the OS is ready to start it which is something we cannot predict hence why thread execution order is unpredictable.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Actually you have your answer. There is no guarantee that when you call a Thread object's Start method that the thread would begin immediately. That's what everyone including me is saying. That thread will start anytime the OS is ready to start it which is something we cannot predict hence why thread execution order is unpredictable.
Code:
Public Sub testthis(ByVal thread As String)
MsgBox(thread.ToString)
End Sub
Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click
Dim testthread As New System.Threading.Thread(AddressOf testthis)
Dim testthread1 As New System.Threading.Thread(AddressOf testthis)
Dim testthread2 As New System.Threading.Thread(AddressOf testthis)
Dim testthread3 As New System.Threading.Thread(AddressOf testthis)
Dim testthread4 As New System.Threading.Thread(AddressOf testthis)
testthread.Start(1)
System.Threading.Thread.Sleep(1500)
testthread1.Start(2)
System.Threading.Thread.Sleep(1500)
testthread2.Start(3)
System.Threading.Thread.Sleep(1500)
testthread3.Start(4)
System.Threading.Thread.Sleep(1500)
testthread4.Start(5)
System.Threading.Thread.Sleep(1500)
Call testthis(6)
End Sub
I don't understand how thread execution order is unpredictable when I can look at the code, predict the order the msgbox numbers will show and see, plain as day, that i've accurately predicted the execution order of my threads. Are you honestly telling me that, when I run threads like this, It's not uncommon to have them show up in some random order when I've yet to see these message boxes show up out of order or anything more than about 1.5 seconds apart?
I don't know about common but its not surprising either. In your original code you're calling on excel objects. Who knows what those calls are doing internally. If for whatever reason they are blocking at any point for any amount of time, it can appear as if the threads haven't begun executing. Maybe excel has its own threading magic going on that is delaying your threads. We really don't know but what is important is that you realize that you have no guarantees about when threads start or switch or how long they execute before they switch which is the only possible explanation for your observations.
If you really want to press this then I suggest you use a more ridiculous time in your original excel code. Use something like 10 seconds for your sleep, observe and tell us what happened. That may yield some clue.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
Also, I noticed in your original code that you're calling that loop3 method multiple times on different threads yet inside the method, it refers to a field at the class/module level. I'd greatly advise you synchronize access to that field via SyncLock. For all we know, that could also cause your problem.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
I don't understand how thread execution order is unpredictable when I can look at the code, predict the order the msgbox numbers will show and see, plain as day, that i've accurately predicted the execution order of my threads. Are you honestly telling me that, when I run threads like this, It's not uncommon to have them show up in some random order when I've yet to see these message boxes show up out of order or anything more than about 1.5 seconds apart?
Yes, we are honestly saying that very thing. Would you like to read a good book on the subject? It is pretty large, but goes into great detail about what is happening in threading under different models. One of the points that you would get from it would be that the actual behavior is different on different versions of Windows. Another point would be that the behavior will be different with a different number of cores in the CPU, but the same OS. A third point would be that the total number of threads (not just in your app, but elsewhere) will change the behavior and can do so in entirely unpredictable ways. For example, the number of threads started will be based on an OS estimation of the number of threads in current use. That estimate could be wrong because there is some OS thread operating that ends soon after the estimation is made, thereby reducing the total thread load on the system, but the OS will then spin up extra threads to optimize the CPU load. However, if one or more threads is active, but is simply blocking (perhaps because of a call to Sleep that causes the thread to pass up its execution window), then the OS will spin up yet more threads, which may cause a problem because the sleeping thread could then wake up, thereby causing some thread (good luck predicting which one) to starve for time until another one completes.
I do think that you are doing something in one of those apps that you are not doing in the other, and that extra thing is partially causing the behavior you are seeing, since you have determined that all the threads are actually starting as expected, but are completing based on what looks like some kind of locking behavior. However, what you are doing is inherently unpredictable, so the fact that it works consistently in one of those apps is nothing but pure chance (and woefully inefficient, at that, since you had to push the sleep times up to get the behavior you are seeing).
Ultimately, you are doing two things wrong:
1) You have an expectation for how threads work that is not based on a sound understanding of what they do. The book I would recommend would fix that, but it's neither small nor light in content.
2) You are using threads in a way that inherently relies on a race condition always coming out the same way. That's a dangerous design, because, even if you got it to work, it would only work most of the time, and the more different the conditions it ran in, the less likely it would be to work the way you expected.
Also, I noticed in your original code that you're calling that loop3 method multiple times on different threads yet inside the method, it refers to a field at the class/module level. I'd greatly advise you synchronize access to that field via SyncLock. For all we know, that could also cause your problem.
But if the OP did that, they might as well throw out the threading in the first place, as they would end up creating an execution train proceeding through the method as if it really was single threaded, so the multithreading would be slower than a single threaded solution. On the other hand, switching to single threading is probably the thing to do anyways.
Yes, we are honestly saying that very thing. Would you like to read a good book on the subject? It is pretty large, but goes into great detail about what is happening in threading under different models. One of the points that you would get from it would be that the actual behavior is different on different versions of Windows. Another point would be that the behavior will be different with a different number of cores in the CPU, but the same OS. A third point would be that the total number of threads (not just in your app, but elsewhere) will change the behavior and can do so in entirely unpredictable ways. For example, the number of threads started will be based on an OS estimation of the number of threads in current use. That estimate could be wrong because there is some OS thread operating that ends soon after the estimation is made, thereby reducing the total thread load on the system, but the OS will then spin up extra threads to optimize the CPU load. However, if one or more threads is active, but is simply blocking (perhaps because of a call to Sleep that causes the thread to pass up its execution window), then the OS will spin up yet more threads, which may cause a problem because the sleeping thread could then wake up, thereby causing some thread (good luck predicting which one) to starve for time until another one completes.
We are talking about two apps running on the same computer with the same OS, same everything.
Originally Posted by Shaggy Hiker
I do think that you are doing something in one of those apps that you are not doing in the other, and that extra thing is partially causing the behavior you are seeing, since you have determined that all the threads are actually starting as expected, but are completing based on what looks like some kind of locking behavior. However, what you are doing is inherently unpredictable, so the fact that it works consistently in one of those apps is nothing but pure chance (and woefully inefficient, at that, since you had to push the sleep times up to get the behavior you are seeing).
I agree that there must be something in one app that's not in the other. I've uploaded the forms/modules of the non-sleeping app and i can upload the forms/modules of the sleeping app and i would LOVE it if somone here could help me learn what's happening here. On both apps the multithreading is filling in columns of data at the same time. On one app the sleep is staggering the order in which that happens and in the other app it does not which is what this thread is about.
Originally Posted by Shaggy Hiker
1) You have an expectation for how threads work that is not based on a sound understanding of what they do. The book I would recommend would fix that, but it's neither small nor light in content.
2) You are using threads in a way that inherently relies on a race condition always coming out the same way. That's a dangerous design, because, even if you got it to work, it would only work most of the time, and the more different the conditions it ran in, the less likely it would be to work the way you expected.
1. what book
2. These threads are working on both apps regardless of what thread makes the workbook visible or which thread opens the workbook so they don't rely on a race condition coming out in the same way. Thead 1 can open the workbook or thread 6 can open the workbook. Either way the workbook is getting opened and the columns are getting filled by all the threads at the same time.
I'm trying to understand what is happening here.
If i'm only on the UI thread and i say
Thread1.start()
Sleep(10000)
'At this point in one application i'm 10 seconds into thread1 and in another application i'm not.
Last edited by iamcpc; Jan 31st, 2013 at 01:40 PM.
If you really want to press this then I suggest you use a more ridiculous time in your original excel code. Use something like 10 seconds for your sleep, observe and tell us what happened. That may yield some clue.
I made some changes to this just to see how it would effect it.
Code:
Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click
Dim testthread As New System.Threading.Thread(AddressOf Module1.testthis)
Dim testthread1 As New System.Threading.Thread(AddressOf Module1.testthis)
Dim testthread2 As New System.Threading.Thread(AddressOf Module1.testthis)
Dim testthread3 As New System.Threading.Thread(AddressOf Module1.testthis)
Dim testthread4 As New System.Threading.Thread(AddressOf Module1.testthis)
testthread.Start(1)
System.Threading.Thread.Sleep(20000)
testthread1.Start(2)
System.Threading.Thread.Sleep(20000)
testthread2.Start(3)
System.Threading.Thread.Sleep(20000)
testthread3.Start(4)
System.Threading.Thread.Sleep(20000)
testthread4.Start(5)
System.Threading.Thread.Sleep(20000)
Call Module1.testthis(6)
End Sub
Public Module Module1
Public loaded As Boolean
Dim xl = CreateObject("excel.application")
Public site1 As String
Dim xl1 As Microsoft.Office.Interop.Excel.Application = New Microsoft.Office.Interop.Excel.Application
Public Sub testthis(ByVal column As Integer)
Debug.Print("before visible " & column.ToString)
If xl1.Visible <> True Then
xl1.Visible = True
Debug.Print("after visible " & column.ToString)
End If
If xl1.ActiveWorkbook Is Nothing Then
xl1.Workbooks.Open("C:\Documents and Settings\BMcGuir1\Desktop\Test\spenddown in mdcd.xlsm")
Debug.Print("after open " & column.ToString)
End If
Dim y As Long
For y = 1 To 100
xl1.ActiveWorkbook.Sheets("sheet1").cells(y, column).value = y
System.Threading.Thread.Sleep(100)
Next
End Sub
End Module
Instead of creating an excel object I created a new exel.application
On the sleeping app only the first thread makes excel visible and only the first thread opens the workbook. When the second thread comes along it says exel is already visible so I won't debug.print anything and excel has already opened a workbook so i won't debug.print anything. I also added 20 second sleeps which caused cause some context switch messages but still all the threads were running at the same time regardless of sleep(20000) I just had to wait 120 seconds to see the excel spreadsheet being filled in.
How some after thread3 has made excel visible. So all the threads are hitting the
if excel visible <> true at the same time. Identifying that excel is not visible and stopping. If this is the case then why am I not getting an error when I try to make an already visible excel visible?
non sleeping app:
before visible 1
before visible 2
before visible 3
before visible 4
before visible 5
after visible 3
after visible 1
after visible 4
after visible 2
after visible 5
after open 6
after open 5
after open 1
after open 2
after open 3
after open 4
On the app where sleep() worksa and i'm sleeping for 2 seconds instead of 20 seconds:
Because the first thread has made exel visible and opened the workbook the other threads don't also make excel visible and open the workbook.
before visible 1
after visible 1
after open 1
before visible 2
before visible 3
before visible 4
before visible 5
before visible 6
Last edited by iamcpc; Jan 31st, 2013 at 12:39 PM.
Reason: added code
Ok. Before the paintings of trolls start coming out in earnest, just one last try.
Program A and Program B do not work alike because there is no reason why they should. It really can't be difficult to grasp this. If Program A works the way you want it to then it is purely a matter of luck and in no way of judgement. It does not have to work that way, it may not always work that way, it just does at present. This is in no way due to your programming 'skills' and, by the same token, it is no way due to a lack of programming skills that Program B does not work the way you want it to. We cannot give you a solution to your 'problems' with Program B because there are none. It does not have to not work that way, it may not always not work that way, it just does at present. And there is nothing that you or anybody else can do about it.
If you want to program with multiple threads you must do so in a way that allows for the inherent unpredictability. That is all there is to it.
Now, you have your answer. Please mark this thread resolved and move on.
As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"
Reviews: "dunfiddlin likes his DataTables" - jmcilhinney
Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!
Program A and Program B do not work alike because there is no reason why they should. It really can't be difficult to grasp this. If Program A works the way you want it to then it is purely a matter of luck and in no way of judgement.
Originally Posted by dunfiddlin
Now, you have your answer. Please mark this thread resolved and move on.
This actually made me laugh. I've come here to find out why the exact code behaves differently in program A than it does in program B and the answer i've been given is because program A is lucky? Why would I mark this thread resolved when your answer is that (when running the same code on the same computer with the same operating system under the same conditions) one program is lucky and one is not lucky.
I've read about programs that start a thread every time an employee makes or recieves a phone call and that thread records how long the phone call lasted. It does this with no unpredictiability. EVERY single call, thousands and thousands and thousands of calls per day have their length recorded as intended using threads of code that are operating under controlled and predicted conditions. The thread starts when the phone call starts, runs when the phone call is active, and the thread ends when the phone call ends.
Not only that but i've taken the code that i've discussed here and put it on 4 new applications and it behaved the same way in ever single application leading me to believe that there is some sort of predictability here.
Last edited by iamcpc; Jan 31st, 2013 at 01:33 PM.
We are talking about two apps running on the same computer with the same OS, same everything.
That doesn't necessarily matter, as there are too many factors within the OS that are unknown. However, by this time, I would assume that you have run both several times each, and the pattern is consistent. That would mean that there really is some difference. Unfortunately, the difference need not be in your program, since you are working with Excel. Every application that is designed for multithreading has to deal with shared resources in some fashion. In your case, the Excel application itself is shared, and you are dealing with it by putting so much distance between threads that you are hoping they won't interact in a bad way. However, Excel could be sharing in some other way. The behavior you have shown, thus far, strongly suggests that there is a critical section somewhere along the path which is allowing only one thread at a time (and that includes the UI thread of the app itself, which is why all the other threads appear to happen later). Since you didn't create a critical section, then the likely culprit is internal to Excel. At that point, any explanation for why one app runs one way and another app runs a different way is pure speculation unless you find an expert on how Excel locks and releases objects. About the only thing you can do is examine the two programs side by side, and for any change in when or how you interact with the Excel object, or any objects within Excel, you could change one of the apps, then test again. Basically, it would be a whole lot of trial and error, because you probably can't tell in any other way what Excel is doing to cause the thread train.
As for the book, I didn't mention the title for a reason: I can't remember it. However, once I get home I'll track it down and post it. It's a very thorough book on the subject. Even if you don't read it all, you would probably find it interesting (maybe not pleasant, since it would strongly discourage the way you are going about this, but interesting nonetheless).
I've read about programs that start a thread every time an employee makes or recieves a phone call and that thread records how long the phone call lasted. It does this with no unpredictiability. EVERY single call, thousands and thousands and thousands of calls per day have their length recorded as intended using threads of code that are operating under controlled and predicted conditions. The thread starts when the phone call starts, runs when the phone call is active, and the thread ends when the phone call ends.
There are certainly a lot of programs written to use multithreading that work well. I've even written a few. However, the situation you describe with the phone calls seems fundamentally different from what you are trying to do. A single thread to deal with a single phone call, whoes lifespan is determined by the phone call, is a very reasonable way to go about things. Predicting the order of completion of those threads would require predicting the order of completion of the phone calls, which is clearly absurd in most cases. You are describing a situation where every thread is independent of any other thread, and can start and finish as needed. This is the way threads should be used. Many (if not nearly all) servers work by using a different thread for every connection request. The connection is handled in that one thread without regard for any other thread on the system, and it ends when it should. All the threads are independent. You can certainly make them dependent, though. All that is required is to have each thread run through some critical section. For instance, if every thread incremented a class level integer variable when it started, then decremented the integer when it finished, you would have to have those increments and decrements performed in critical sections, or the integer would soon be meaningless (in a multithreaded system, a statement like N += 1 could result in N, N+1, or N+X, where X is an arbitrarily large number). Once you have critical sections in multthreaded code, every thread will proceed through that critical section in the order that they arrive, and only when no other thread is in the critical section. At that point, your threads are interrelated, which is a much harder problem to solve.
In your case, your threads are interrelated intentionally. You want one thread to run at a time determined by threads around it (basically, one thread runs after another thread has finished). Furthermore, the behavior you are seeing certainly looks like the threads are stacking up at a critical section. However, you didn't create a critical section (you don't synchlock, monitor, grab a Mutex, or otherwise restrict) anything, so the critical section is probably in Excel itself. Figuring out where it is would be just trial and error...and kind of meaningless, since you'd be hard pressed to do anything useful with the information once you had it.
Not only that but i've taken the code that i've discussed here and put it on 4 new applications and it behaved the same way in ever single application leading me to believe that there is some sort of predictability here.
That's a good step. So, now you have 5 that work in fashion A, and one that works in fashion B. The easiest thing to do would be to compare group A to group B, and see what is different about when things are done, and in what order, then try turning one of the group A into group B by changing just one thing at a time.
One thing that you have already determined is that all the threads are starting. That's what the breakpoints showed you. Further investigation along that line could show you at what line they stall while waiting for the UI thread to finish. That might help with figuring out what is different between A and B...but it might not, too. The threads are all running, they just get bottlenecked on some call in Excel, but only in some situations. The trick will be figuring out which situations cause the bottleneck, which might allow you to formulate a theory as to what Excel is locking on. The most likely situation is that Excel is locking some object (which you quite likely can't see, but it would look like it was locking a spreadsheet, workbook, app, or some other object you CAN see) and only one thread at a time can hold that lock. Therefore, the UI thread acquires the lock early on. Then, each subsequent thread proceeds until it gets to the point where it tries to acquire the lock, but since the UI thread already has it, the other thread goes to sleep. This happens for each thread until they are all sleeping at the point where they need a lock that the UI thread holds. Then the UI thread releases the lock, and the OS wakes up one thread (which one is not up to you, and isn't necessarily the first one in line), which acquires the lock (alternatively, they may ALL wake up, but only one gets the lock, so all the others go back to sleep), and proceeds. Once that thread releases the lock, the next thread acquires it, and so on.
The most reasonable place for the lock to occur is either on the workbook, or the cells. Moreover, the behavior you are seeing suggests that the lock is acquired before the line that writes, then released right after. Since the lock probably isn't really any of the objects you see (that could be dangerous), but is actually an object internal to Excel that you don't see (and may only exist for the purpose of locking), there really isn't anything you can say about it. The real question is why Excel is locking in one app and not in another, but I'd be a bit surprised if anybody around here would know what strategy the Excel team at MS used for synchronization of multiple users against a single spreadhseet.
That's a good step. So, now you have 5 that work in fashion A, and one that works in fashion B. The easiest thing to do would be to compare group A to group B, and see what is different about when things are done, and in what order, then try turning one of the group A into group B by changing just one thing at a time.
I FIGURED IT OUT!!! OMG BEST ADVICE EVER.
OK the non sleep() sub there is a form1.webbrowser1 document completed event handler setting a global boolean variable in module 1 loaded to true if the document is completed and to false if the webbrowser is navigated or navigating. Why is this affecting how sleep() works? When I removed that changing of the value of the module 1 variable it works like my other 5 applications.
Why would this form1.webbrowser1 document completed event handler affect another thread?
Why would global variables affect how sleep works()
Is there a way that I can use this global variable and the webbrowser document completed event in a way that does not effect my threads and how sleep() works?
Last edited by iamcpc; Jan 31st, 2013 at 03:09 PM.