|
web
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
thread dead lockI've got a class which performs some lengthy process in a background (separate) thread. And this lengthy process raises events regularly to inform the main thread of the progress (which is then displayed to the user). Since the event is raised from another thread, i've used me.Invoke() to update the ui properly. However, my problem is in cancelling the process. I need to be able to execute a method such as Cancel() which will return only when the process has been cancelled. Below is the code i've implemented. How ever, the WaitOne() method will ALWAYS return FALSE. I'm assuming this is becuase the WaitOne causes the current thread to wait untill it receives a signal. & since the current thread is also in the process of updating the UI, the background process thread is not able to send the signal. Hence a dead lock (if not for the timeout in the WaitOne()) I'm sure this HAS to be possible. I need a mechanism to cancel the background process and WAIT till it confirms that the process has been cancelled (i.e. wait till the process notifies the main thread that the process has been cancelled). How can this be done? Below is a sample the code which outlies the above issue: Public Class frmThreads Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " Public Sub New() MyBase.New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call End Sub 'Form overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Required by the Windows Form Designer Private components As System.ComponentModel.IContainer 'NOTE: The following procedure is required by the Windows Form Designer 'It can be modified using the Windows Form Designer. 'Do not modify it using the code editor. Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents Button2 As System.Windows.Forms.Button Friend WithEvents Label1 As System.Windows.Forms.Label <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Button1 = New System.Windows.Forms.Button Me.Button2 = New System.Windows.Forms.Button Me.Label1 = New System.Windows.Forms.Label Me.SuspendLayout() ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(40, 120) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 0 Me.Button1.Text = "Button1" ' 'Button2 ' Me.Button2.Location = New System.Drawing.Point(128, 120) Me.Button2.Name = "Button2" Me.Button2.TabIndex = 1 Me.Button2.Text = "Button2" ' 'Label1 ' Me.Label1.Location = New System.Drawing.Point(32, 24) Me.Label1.Name = "Label1" Me.Label1.TabIndex = 2 Me.Label1.Text = "Label1" ' 'frmThreads ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.Label1) Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.Button1) Me.Name = "frmThreads" Me.Text = "frmThreads" Me.ResumeLayout(False) End Sub #End Region Private _lengthyProcess As LengthyProcess Private _counter As Integer Private dlgIvokeProgress As New dlgProgress(AddressOf ShowProgress) Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click _lengthyProcess = New LengthyProcess AddHandler _lengthyProcess.Progress, AddressOf LengthyProcess_Progress Dim start As New Threading.Thread(AddressOf StartProcess) start.Start() End Sub Private Sub StartProcess() _lengthyProcess.Start() End Sub Private Sub LengthyProcess_Progress(ByVal counter As Integer) _counter = counter Me.Invoke(dlgIvokeProgress) End Sub Private Delegate Sub dlgProgress() Private Sub ShowProgress() Me.Label1.Text = _counter End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click MsgBox(_lengthyProcess.Cancel) End Sub End Class Public Class LengthyProcess Public Event Progress(ByVal counter As Integer) Private _counter As Integer Private _cancelled As Boolean Private _stopped As Threading.ManualResetEvent Public Sub Start() Do While _cancelled = False _counter += 1 Threading.Thread.Sleep(100) RaiseEvent Progress(_counter) Loop If _cancelled = True Then _stopped.Set() End If End Sub Public Function Cancel() As Boolean _stopped = New Threading.ManualResetEvent(False) _cancelled = True 'this will ALWAYS return FALSE 'how ever if i were to display a messagebox here and THEN 'execute the WaitOne(), it will return TRUE 'MsgBox("WaitOne will now return TRUE") Return _stopped.WaitOne(5000, False) End Function End Class I think that you've tried to over-engineer it a tad and in the process have
confused yourself. Try this very simple code and then take it from there: Note that the thread is now completely encapsulated within the LengthyProcess class and the calling process does not need to know anything about the thread. It only needs to know that the LengthyProcess class has a Start method, a Cancel method and raises a Progress event. In the LengthyProcess.Cancel method the call to _process.Join() has the effect of waiting until the thread has actually finished which should be somewhere up to 100 milliseconds plus the overhead for raising the Progress event after the _cancel variable is set. Also note that your UI thread (the form) will block each time the Progress event is raised for as long at it takes to execute the ShowProgress method. This means that ShowProgress must be as lean and mean as you can make it. As an alternatively you could use BeginInvoke instead of Invoke which has the effect of sending the ShowProgress method off to do it's stuff and immediately allowing the LengthyProcess thread to continue. <snip>all the preamble</snip Private _lengthyProcess As LengthyProcess Private _counter As Integer Private Delegate Sub dlgProgress() Private dlgIvokeProgress As New dlgProgress(AddressOf ShowProgress) Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click _lengthyProcess = New LengthyProcess AddHandler _lengthyProcess.Progress, AddressOf LengthyProcess_Progress _lengthyProcess.Start() End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click _lengthyProcess.Cancel End Sub Private Sub LengthyProcess_Progress(ByVal counter As Integer) _counter = counter Invoke(dlgIvokeProgress) End Sub Private Sub ShowProgress() Label1.Text = _counter.ToString() Label1.Update() End Sub End Class Public Class LengthyProcess Public Event Progress(ByVal counter As Integer) Private _counter As Integer = 0 Private _process as Thread = Nothing Private _cancel as Boolean = False Public Sub Start() _process = New Thread(AddressOf TheProcess) _process.Start() End Sub Public Sub Cancel() _cancel = True _process.Join() End Sub Private Sub TheProcess() While Not _cancel _counter += 1 Thread.Sleep(100) RaiseEvent Progress(_counter) End While End Sub End Class Show quoteHide quote "Don" <D**@discussions.microsoft.com> wrote in message news:B71E1C98-EB63-486E-AA86-1DF9DD0123A9@microsoft.com... > How to stop a process which is running in a separate thread!!! > > I've got a class which performs some lengthy process in a background > (separate) thread. And this lengthy process raises events regularly to > inform > the main thread of the progress (which is then displayed to the user). > Since the event is raised from another thread, i've used me.Invoke() to > update the ui properly. > > However, my problem is in cancelling the process. > I need to be able to execute a method such as Cancel() which will return > only when the process has been cancelled. > Below is the code i've implemented. > > How ever, the WaitOne() method will ALWAYS return FALSE. > I'm assuming this is becuase the WaitOne causes the current thread to wait > untill it receives a signal. & since the current thread is also in the > process of updating the UI, the background process thread is not able to > send > the signal. > Hence a dead lock (if not for the timeout in the WaitOne()) > > I'm sure this HAS to be possible. > I need a mechanism to cancel the background process and WAIT till it > confirms that the process has been cancelled (i.e. wait till the process > notifies the main thread that the process has been cancelled). > > How can this be done? > > Below is a sample the code which outlies the above issue: > > > Public Class frmThreads > Inherits System.Windows.Forms.Form > > #Region " Windows Form Designer generated code " > > Public Sub New() > MyBase.New() > > 'This call is required by the Windows Form Designer. > InitializeComponent() > > 'Add any initialization after the InitializeComponent() call > > End Sub > > 'Form overrides dispose to clean up the component list. > Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) > If disposing Then > If Not (components Is Nothing) Then > components.Dispose() > End If > End If > MyBase.Dispose(disposing) > End Sub > > 'Required by the Windows Form Designer > Private components As System.ComponentModel.IContainer > > 'NOTE: The following procedure is required by the Windows Form Designer > 'It can be modified using the Windows Form Designer. > 'Do not modify it using the code editor. > Friend WithEvents Button1 As System.Windows.Forms.Button > Friend WithEvents Button2 As System.Windows.Forms.Button > Friend WithEvents Label1 As System.Windows.Forms.Label > <System.Diagnostics.DebuggerStepThrough()> Private Sub > InitializeComponent() > Me.Button1 = New System.Windows.Forms.Button > Me.Button2 = New System.Windows.Forms.Button > Me.Label1 = New System.Windows.Forms.Label > Me.SuspendLayout() > ' > 'Button1 > ' > Me.Button1.Location = New System.Drawing.Point(40, 120) > Me.Button1.Name = "Button1" > Me.Button1.TabIndex = 0 > Me.Button1.Text = "Button1" > ' > 'Button2 > ' > Me.Button2.Location = New System.Drawing.Point(128, 120) > Me.Button2.Name = "Button2" > Me.Button2.TabIndex = 1 > Me.Button2.Text = "Button2" > ' > 'Label1 > ' > Me.Label1.Location = New System.Drawing.Point(32, 24) > Me.Label1.Name = "Label1" > Me.Label1.TabIndex = 2 > Me.Label1.Text = "Label1" > ' > 'frmThreads > ' > Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) > Me.ClientSize = New System.Drawing.Size(292, 266) > Me.Controls.Add(Me.Label1) > Me.Controls.Add(Me.Button2) > Me.Controls.Add(Me.Button1) > Me.Name = "frmThreads" > Me.Text = "frmThreads" > Me.ResumeLayout(False) > > End Sub > > #End Region > > Private _lengthyProcess As LengthyProcess > Private _counter As Integer > Private dlgIvokeProgress As New dlgProgress(AddressOf ShowProgress) > > Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As > System.EventArgs) Handles Button1.Click > _lengthyProcess = New LengthyProcess > AddHandler _lengthyProcess.Progress, AddressOf > LengthyProcess_Progress > > Dim start As New Threading.Thread(AddressOf StartProcess) > start.Start() > End Sub > > Private Sub StartProcess() > _lengthyProcess.Start() > End Sub > > Private Sub LengthyProcess_Progress(ByVal counter As Integer) > _counter = counter > Me.Invoke(dlgIvokeProgress) > End Sub > > Private Delegate Sub dlgProgress() > Private Sub ShowProgress() > Me.Label1.Text = _counter > End Sub > > Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As > System.EventArgs) Handles Button2.Click > MsgBox(_lengthyProcess.Cancel) > End Sub > End Class > > Public Class LengthyProcess > > Public Event Progress(ByVal counter As Integer) > > Private _counter As Integer > Private _cancelled As Boolean > Private _stopped As Threading.ManualResetEvent > > Public Sub Start() > Do While _cancelled = False > _counter += 1 > > Threading.Thread.Sleep(100) > RaiseEvent Progress(_counter) > Loop > > If _cancelled = True Then > _stopped.Set() > End If > End Sub > > Public Function Cancel() As Boolean > _stopped = New Threading.ManualResetEvent(False) > _cancelled = True > > > 'this will ALWAYS return FALSE > 'how ever if i were to display a messagebox here and THEN > 'execute the WaitOne(), it will return TRUE > 'MsgBox("WaitOne will now return TRUE") > > Return _stopped.WaitOne(5000, False) > End Function > End Class Using BeginInvoke() did the trick
Thanks. Show quoteHide quote "Stephany Young" wrote: > I think that you've tried to over-engineer it a tad and in the process have > confused yourself. Try this very simple code and then take it from there: > > Note that the thread is now completely encapsulated within the > LengthyProcess class and the calling process does not need to know anything > about the thread. It only needs to know that the LengthyProcess class has a > Start method, a Cancel method and raises a Progress event. > > In the LengthyProcess.Cancel method the call to _process.Join() has the > effect of waiting until the thread has actually finished which should be > somewhere up to 100 milliseconds plus the overhead for raising the Progress > event after the _cancel variable is set. > > Also note that your UI thread (the form) will block each time the Progress > event is raised for as long at it takes to execute the ShowProgress method. > This means that ShowProgress must be as lean and mean as you can make it. As > an alternatively you could use BeginInvoke instead of Invoke which has the > effect of sending the ShowProgress method off to do it's stuff and > immediately allowing the LengthyProcess thread to continue. > > <snip>all the preamble</snip > > Private _lengthyProcess As LengthyProcess > Private _counter As Integer > Private Delegate Sub dlgProgress() > Private dlgIvokeProgress As New dlgProgress(AddressOf ShowProgress) > > Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As > System.EventArgs) Handles Button1.Click > _lengthyProcess = New LengthyProcess > AddHandler _lengthyProcess.Progress, AddressOf LengthyProcess_Progress > _lengthyProcess.Start() > End Sub > > Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As > System.EventArgs) Handles Button2.Click > _lengthyProcess.Cancel > End Sub > > Private Sub LengthyProcess_Progress(ByVal counter As Integer) > _counter = counter > Invoke(dlgIvokeProgress) > End Sub > > Private Sub ShowProgress() > Label1.Text = _counter.ToString() > Label1.Update() > End Sub > > End Class > > Public Class LengthyProcess > > Public Event Progress(ByVal counter As Integer) > Private _counter As Integer = 0 > Private _process as Thread = Nothing > Private _cancel as Boolean = False > > Public Sub Start() > _process = New Thread(AddressOf TheProcess) > _process.Start() > End Sub > > Public Sub Cancel() > _cancel = True > _process.Join() > End Sub > > Private Sub TheProcess() > While Not _cancel > _counter += 1 > Thread.Sleep(100) > RaiseEvent Progress(_counter) > End While > End Sub > > End Class > > > "Don" <D**@discussions.microsoft.com> wrote in message > news:B71E1C98-EB63-486E-AA86-1DF9DD0123A9@microsoft.com... > > How to stop a process which is running in a separate thread!!! > > > > I've got a class which performs some lengthy process in a background > > (separate) thread. And this lengthy process raises events regularly to > > inform > > the main thread of the progress (which is then displayed to the user). > > Since the event is raised from another thread, i've used me.Invoke() to > > update the ui properly. > > > > However, my problem is in cancelling the process. > > I need to be able to execute a method such as Cancel() which will return > > only when the process has been cancelled. > > Below is the code i've implemented. > > > > How ever, the WaitOne() method will ALWAYS return FALSE. > > I'm assuming this is becuase the WaitOne causes the current thread to wait > > untill it receives a signal. & since the current thread is also in the > > process of updating the UI, the background process thread is not able to > > send > > the signal. > > Hence a dead lock (if not for the timeout in the WaitOne()) > > > > I'm sure this HAS to be possible. > > I need a mechanism to cancel the background process and WAIT till it > > confirms that the process has been cancelled (i.e. wait till the process > > notifies the main thread that the process has been cancelled). > > > > How can this be done? > > > > Below is a sample the code which outlies the above issue: > > > > > > Public Class frmThreads > > Inherits System.Windows.Forms.Form > > > > #Region " Windows Form Designer generated code " > > > > Public Sub New() > > MyBase.New() > > > > 'This call is required by the Windows Form Designer. > > InitializeComponent() > > > > 'Add any initialization after the InitializeComponent() call > > > > End Sub > > > > 'Form overrides dispose to clean up the component list. > > Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) > > If disposing Then > > If Not (components Is Nothing) Then > > components.Dispose() > > End If > > End If > > MyBase.Dispose(disposing) > > End Sub > > > > 'Required by the Windows Form Designer > > Private components As System.ComponentModel.IContainer > > > > 'NOTE: The following procedure is required by the Windows Form Designer > > 'It can be modified using the Windows Form Designer. > > 'Do not modify it using the code editor. > > Friend WithEvents Button1 As System.Windows.Forms.Button > > Friend WithEvents Button2 As System.Windows.Forms.Button > > Friend WithEvents Label1 As System.Windows.Forms.Label > > <System.Diagnostics.DebuggerStepThrough()> Private Sub > > InitializeComponent() > > Me.Button1 = New System.Windows.Forms.Button > > Me.Button2 = New System.Windows.Forms.Button > > Me.Label1 = New System.Windows.Forms.Label > > Me.SuspendLayout() > > ' > > 'Button1 > > ' > > Me.Button1.Location = New System.Drawing.Point(40, 120) > > Me.Button1.Name = "Button1" > > Me.Button1.TabIndex = 0 > > Me.Button1.Text = "Button1" > > ' > > 'Button2 > > ' > > Me.Button2.Location = New System.Drawing.Point(128, 120) > > Me.Button2.Name = "Button2" > > Me.Button2.TabIndex = 1 > > Me.Button2.Text = "Button2" > > ' > > 'Label1 > > ' > > Me.Label1.Location = New System.Drawing.Point(32, 24) > > Me.Label1.Name = "Label1" > > Me.Label1.TabIndex = 2 > > Me.Label1.Text = "Label1" > > ' > > 'frmThreads > > ' > > Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) > > Me.ClientSize = New System.Drawing.Size(292, 266) > > Me.Controls.Add(Me.Label1) > > Me.Controls.Add(Me.Button2) > > Me.Controls.Add(Me.Button1) > > Me.Name = "frmThreads" > > Me.Text = "frmThreads" > > Me.ResumeLayout(False) > > > > End Sub > > > > #End Region > > > > Private _lengthyProcess As LengthyProcess > > Private _counter As Integer > > Private dlgIvokeProgress As New dlgProgress(AddressOf ShowProgress) > > > > Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As > > System.EventArgs) Handles Button1.Click > > _lengthyProcess = New LengthyProcess > > AddHandler _lengthyProcess.Progress, AddressOf > > LengthyProcess_Progress > > > > Dim start As New Threading.Thread(AddressOf StartProcess) > > start.Start() > > End Sub > > > > Private Sub StartProcess() > > _lengthyProcess.Start() > > End Sub > > > > Private Sub LengthyProcess_Progress(ByVal counter As Integer) > > _counter = counter > > Me.Invoke(dlgIvokeProgress) > > End Sub > > > > Private Delegate Sub dlgProgress() > > Private Sub ShowProgress() > > Me.Label1.Text = _counter > > End Sub > > > > Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As > > System.EventArgs) Handles Button2.Click > > MsgBox(_lengthyProcess.Cancel) > > End Sub > > End Class > > > > Public Class LengthyProcess > > > > Public Event Progress(ByVal counter As Integer) > > > > Private _counter As Integer > > Private _cancelled As Boolean > > Private _stopped As Threading.ManualResetEvent > > > > Public Sub Start() > > Do While _cancelled = False > > _counter += 1 > > > > Threading.Thread.Sleep(100) > > RaiseEvent Progress(_counter) > > Loop > > > > If _cancelled = True Then > > _stopped.Set() > > End If > > End Sub > > > > Public Function Cancel() As Boolean > > _stopped = New Threading.ManualResetEvent(False) > > _cancelled = True > > > > > > 'this will ALWAYS return FALSE > > 'how ever if i were to display a messagebox here and THEN > > 'execute the WaitOne(), it will return TRUE > > 'MsgBox("WaitOne will now return TRUE") > > > > Return _stopped.WaitOne(5000, False) > > End Function > > End Class > > >
While not starting a debate BUT
Passing multiple arguments to the client-side JavaScript function in AJAX textbox help VB2005 Replace " character in string PRINTER INFORMATION Save Changes in VB.NET 2005 with Access 2003 --- Not using any Binding Navigator textbox.backgroundImage ConfigurationManager unrecognized vb2005 express textbox control Hide property of component |
|||||||||||||||||||||||