GornHorse Posted October 25, 2004 Posted October 25, 2004 Hi There, I have a thread which executes a class. I have another thread which starts a timer (system.timers.time). I also have a textbox on the form which, when the elapsed event of the timer is called, will increment. Basically, what I want to be able to do is graphically represent to the user that the process is running (because this process can take several hours to run), and to display the elapsed time. The problem I have, is that the class thread starts, and then the timer thread starts; and the timer clicks over a few increments, and then doesn't get called for a while (while the class is being executed), and then, once the class has finished executing, the timer 'catches up', and quickly increments to the actual elapsed time, and then all threads are stopped as a result of calling the FinishedLoading event handler. I don't know how I should call get this working so that the timer can continue to increment whilst the class is being executed. My code snippet is as follows: public Thread1 as Threading.Thread public ThreadTimer as Threading.Thread private event FinishedLoading() dim Timer as Timers.Timer Public Sub New(byval parent as frmMain) Thread1 = new System.Threading.Thread(AddressOf CreateDDBatch) ThreadTimer = new System.Threading.Thread(AddressOf Timer1Start) AddHandler FinishedLoading, AddressOf FinishedLoadingEH Thread1.Start() ThreadTimer.Start() End Sub Private Sub CreateDDBatch() Dim DDProcessor as new clsID_DD_AutoProcessor(username, password) RaiseEvent FinishedLoading() End Sub Sub FinishedLoadingEH() ThreadTimer.Abort() Timer1.Stop() Me.Cursor = Windows.Forms.Cursors.Default Me.btnClose.Enabled = True Thread1.Abort() End Sub Private Sub Timer1Start() Timer = New System.Timers.Timer(100) AddHandler Timer.Elapsed, AddressOf TimerTick Timer.Start() End Sub Private Sub TimerTick(ByVal sender as Object, ByVal e as Timers.ElapsedEventArgs) Try tbTimer.text = cint(tbTimer.text) + 1 Catch ex as Exception End Try End Sub Please try and respond asap with some assistance. Thanks very much, Michelle Quote
GornHorse Posted October 26, 2004 Author Posted October 26, 2004 Further to problem... Hi there. Well, I have figured out what the problem is, but don't know how to fix it!! I have slightly modified the code from above, such that the timer does not run on a separate thread - i just call Timer1Start() instead of ThreadTimer.Start(). Now, in remembering that, the timer does appear to handle the timer.elapsed event continually whilst the thread is running (calling the class). The reason that I know this is because I created a small console application using exactly the same code as my windows app, and dumped to the console a) when the thread begins, b) an incrementing int when the timer.elapsed event is called, and, c) when the thread finishes. This shows the output as... Starting the Thread... Starting the Timer... **************** Calling the Class 1 2 3 4 . . . 7428 7429 Finished Calling the Class. Therefore, the timer does increment whilst the thread is running. My problem now appears to be that when the timer.elapsed event is handles, I want a textbox on my windows form to increment each time the event is handled so the user knows something is happening. What is happening, though, is that once the class is called, the textbox stops refreshing with the new value. It only catches up once the class is finished being called. I tried using the following lines of code, which, according to other threads, should have worked: Timer.SynchronizingObject = Me and Timer.SynchronizingObject = tbTimer But, neither of them made the program run any differently. How on earth can I get the tbTimer textbox to display the increments each time the timer.elapsed event is handled - because, the event is being handled, the textbox just isn't displaying the new text. Thanks in advance, Michelle PS: Please respond ASAP, this is rather urgent!! Quote
ToniMontana Posted October 26, 2004 Posted October 26, 2004 (edited) Hi Michelle, for me it is a little bit unclear what you want do do with your code. You say "executing the class".... !?!? My idea is: Try to make another project in which you start again with a very easy version of that problem. Threading can be a difficult thing and you must bear in mind, that you cannot change a control (e.g. a textbox) from another thread so easy. You HAVE TO USE BeginInvoke() or Invoke() to make changes to the so called "UI-thread", that is the thread, in which all your controls run. Try to read some things in the net: http://www.yoda.arachsys.com/csharp/multithreading.html http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/netcfmultithreadedapp.asp Info from MSDN: >> Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control's method from a different thread, you must use one of the control's invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control. There are four methods on a control that are safe to call from any thread: Invoke, BeginInvoke, EndInvoke and CreateGraphics. For all other method calls, you should use one of these invoke methods when calling from a different thread. Windows Forms uses the single-threaded apartment (STA) model because Windows Forms is based on native Win32 windows that are inherently apartment-threaded. The STA model implies that a window can be created on any thread, but it cannot switch threads once created, and all function calls to it must occur on its creation thread. Outside Windows Forms, classes in the .NET Framework use the free threading model. << Edited October 26, 2004 by ToniMontana Quote Greetings, Toni.
GornHorse Posted October 26, 2004 Author Posted October 26, 2004 Hi Toni, To explain what it is that I am trying to do... I have a thread, call this ThreadClassRunner. When the ThreadClassRunner starts, it runs a particular function from a referenced class. I also have a Timers.Timer that is to tick over to calculate the elapsed time taken for the function to complete - this can be up to several hours. I have a textbox on my form that I want to update each time the timer.elapsed event fires so that the users are aware of a) the fact that the function is still running, and b) to inform them of how long the function is taking to run. I know that the elapsed event continues to fire while the ThreadClassRunner is running because i a) created a console application that dumped output to the screen so I know what was happening, and this indicated that yes, the elapsed event continues to be handled correctly while the ThreadClassRunner is running. And, b) within my actual application, i ran a trace to the event log, and this also indicated that the elapsed event was correctly being handled while the ThreadClassRunner was running. So, the timer is correctly handling the elapsed event. My problem is that the textbox (which i want the elapsed time to display in) is not refreshing each time the elapsed event is being handled. I can't understand WHY this is happening, and I haven't been able to find to much info about how to fix it. I don't understand how the textbox.Invoke() method works, and therefore, would really appreciate some more help with this. Thanks. BTW, I have been looking though the info that you provided links to, and it just seems to be help on the threading. I have the threading working - i just need to get the display working. Please remember that I do not create the timer on a thread, therefore, I would assume that the elapsed event is not being handled on a thread either. The only object in the application that is running on a thread is the class's function which runs on the ThreadClassRunner. Quote
Diesel Posted October 26, 2004 Posted October 26, 2004 When you create a new thread, the main thread is still running. The timer does not and should not have it's own thread. Simple create a new thread for the worker function as you are and then create a loop that checks to see if that thread has finished, if not print the elapsed time to a label: public Thread1 as Threading.Thread private event FinishedLoading() dim Timer as Timers.Timer Public Sub New(byval parent as frmMain) Thread1 = new System.Threading.Thread(AddressOf CreateDDBatch) AddHandler FinishedLoading, AddressOf FinishedLoadingEH Timer = New System.Timers.Timer() Timer.Start() Thread1.Start() While (Thread1.Join(1000)) 'this next line may be wrong, edit tbTimer.Text = "Hours: " + Cstr(Date.Now.Hours - Timer.Hours) + " Minutes: " + Cstr(Date.Now.Minutes - Timer.Minutes) + " Seconds: " + Cstr(Date.Now.Seconds - Timer.Second) End While End Sub Private Sub CreateDDBatch() Dim DDProcessor as new clsID_DD_AutoProcessor(username, password) RaiseEvent FinishedLoading() End Sub Sub FinishedLoadingEH() Me.Cursor = Windows.Forms.Cursors.Default Me.btnClose.Enabled = True End Sub Quote
Diesel Posted October 26, 2004 Posted October 26, 2004 It might by While (NOT (Thread1.Join(1000))), I forget what it returns, try both Quote
Joe Mamma Posted October 26, 2004 Posted October 26, 2004 you guys are making this too hard. . . look up 'Asynchronous Programming Design Pattern' in the help use C#. . . vb blows Quote Joe Mamma Amendment 4: The right of the people to be secure in their persons, houses, papers, and effects, against unreasonable searches and seizures, shall not be violated, and no warrants shall issue, but upon probable cause, supported by oath or affirmation, and particularly describing the place to be searched, and the persons or things to be seized. Amendment 9: The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.
HJB417 Posted October 26, 2004 Posted October 26, 2004 you guys are making this too hard. . . look up 'Asynchronous Programming Design Pattern' in the help use C#. . . vb blows use managed c++, c# blows Quote
Joe Mamma Posted October 26, 2004 Posted October 26, 2004 use managed c++' date=' c# blows[/quote']unless your working with clients who dont code Quote Joe Mamma Amendment 4: The right of the people to be secure in their persons, houses, papers, and effects, against unreasonable searches and seizures, shall not be violated, and no warrants shall issue, but upon probable cause, supported by oath or affirmation, and particularly describing the place to be searched, and the persons or things to be seized. Amendment 9: The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.
GornHorse Posted October 26, 2004 Author Posted October 26, 2004 Hi there... Ok, so there have been a few bits of info here... but, I am a bit confused now. I know what I need to do, and that is to get the textbox to display the elapsed time each time the timer.elapsed event is handled. Forget about the threading - I've got that working. I just need a piece of code to help me get the textbox to display the text properly. Now, I am assuming from what I have read that I need to use the textbox.Invoke() method, but, I have no idea what to do with it! I have to pass it a system.delegate, but what is it? You've seen my code above, should I pass it one of those methods? Please help me with this - I really need to get this working. Thanks, Michelle Quote
GornHorse Posted October 27, 2004 Author Posted October 27, 2004 Ok... So, to probably make things worse... this is my new code. Bare in mind that this is still not making the textbox display the text each time the timer.elapsed event is handled, but I think i'm headed in the right direction - but I definately still need more help!! Dim myMainFrm As MADUSA_MainMenu Public Thread1 As Threading.Thread Dim MADConnector As clsMADConnObj.Connection Private Event FinishedLoading() Dim curtime As DateTime Dim Timer As Timers.Timer Dim t_StartTime As DateTime Dim t As TimeSpan Dim m_counter As Integer Public Sub New(ByVal parent As MADUSA_MainMenu) MyBase.New() myMainFrm = parent MADConnector = myMainFrm.GetMADConnector 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call Thread1 = New System.Threading.Thread(AddressOf CreateDDBatch) Thread1.Name = "Create DD Batch Thread" AddHandler FinishedLoading, AddressOf FinishedLoadingEH t_StartTime = System.DateTime.Now Timer1Start() Thread1.Start() End Sub Private Sub CreateDDBatch() Dim DDProcessor As New clsID_DirectDebit_AutoProcessor.AutoProcessor(MADConnector, myMainFrm.GetUsername, myMainFrm.GetPassword) RaiseEvent FinishedLoading() End Sub Sub FinishedLoadingEH() Timer.Enabled = False Timer.Stop() Me.Cursor = Windows.Forms.Cursors.Default Me.btnClose.Enabled = True Thread1.Abort() End Sub Private Sub Timer1Start() Timer = New System.Timers.Timer(500) Timer.Enabled = True Timer.SynchronizingObject = myMainFrm AddHandler Timer.Elapsed, AddressOf TimerTick Timer.Enabled = True Timer.Start() End Sub Private Sub TimerTick(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Try Dim t_CurrentTime As DateTime = e.SignalTime Dim t_ElapsedTime As TimeSpan = t_CurrentTime.Subtract(t_StartTime) t = t_ElapsedTime UseDelegate() Catch ex As Exception End Try End Sub Delegate Sub UpdateTextbox(ByVal str As String) Private Function UseDelegate() Dim objUpdater As New Updater(Me) Dim objDelegator As UpdateTextbox objDelegator = New UpdateTextbox(AddressOf objUpdater.WriteSomething) objDelegator.Invoke(t.ToString) End Function Class Updater Dim sub_Parent As MADUSA_DirectDebitProcess_Timer Public Sub New(ByVal subp As MADUSA_DirectDebitProcess_Timer) sub_Parent = subp End Sub Public Sub WriteSomething(ByVal strToWrite As String) sub_Parent.tbTimer.Text = strToWrite End Sub End Class Perhaps with this code, someone may be able to give me a hint??!! Thanks again, Michelle Quote
Diesel Posted October 27, 2004 Posted October 27, 2004 I was able to get a sample program working, I used a System.Windows.Forms.Timer Quote
GornHorse Posted October 27, 2004 Author Posted October 27, 2004 I was able to get a sample program working' date=' I used a System.Windows.Forms.Timer[/quote'] Hi Diesel, Could you please provide me with the code that you got to work? Thanks, Michelle Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.