thread dead lock

G

Guest

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
 
S

Stephany Young

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
 
G

Guest

Using BeginInvoke() did the trick
Thanks.


Stephany Young said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top