Technique for Pausing Worker Thread

C

Charles Law

My first thought was to call

WorkerThread.Suspend

but the help cautions against this (for good reason) because the caller has
no control over where the thread actually stops, and it might have a lock
pending, for example.

I want to be able to stop a thread temporarily, and then optionally resume
it or stop it for good.

The code in the worker thread calls a recursive function, so passing some
flag down to the level at which the code is executing is quite messy. It
also calls methods on other objects that are defined locally, so a top-level
Pause method would not readily have a handle to one of the secondary
objects.

I should add that the worker thread code is contained in a worker thread
class, so what I am actually doing is something like

Dim wc as New WorkerClass

wc.StartThread ' starts worker thread and returns

' Do stuff
....

wc.Pause

It is this Pause that needs to stop the worker thread as soon as possible,
but at a sensible point.

Can anyone suggest a good technique for doing this? A pattern maybe?

A secondary question is: what should the thread do whilst it is paused?
Would it be reasonable for the thread to call Suspend on itself once it has
received the pause trigger? At least it would be able to execute the suspend
in a sensible place.

TIA

Charles
[I know ... you don't hear from me for ages and then suddenly the flood
gates open]
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
Is the worker thread going to pause itself or is another thread going to
pause the worker thread?

If another thread is going to pause the worker thread, my first choice would
be to use Thread.Suspend & Thread.Resume. However understanding the caveat
you identified.
It is this Pause that needs to stop the worker thread as soon as possible,
but at a sensible point.
I would make WorkerClass.Pause set an instance level variable, then at
opportune times the WorkerClass itself would call Thread.Suspend based on
this variable...

I would then make WorkerClass.UnPause check to see if the Worker Thread
itself is suspended & call Thread.Resume.

Is the recursive function part of WorkerClass or part of the other objects?
If its part of WorkerClass it can easily check a instance level variable. As
you mention the other objects won't be able to check this variable.

An alternative to Thread.Suspend & Thread.Resume might be a
ManualResetEvent, where WorkerClass.Pause Resets the event, and
WorkerClass.UnPause sets the event, and at opportune times the WorkerClass
itself would WaitOne on the event. (thinking about it I might prefer the
ManualResetEvent over a different variable & Thread.Suspend &
Thread.Resume...).

A more advanced alternative might be using a shared field with
System.ThreadStaticAttribute in your WorkerClass you might be able to give
the other objects an opportune time to suspend the thread, however the code
might get rather involved on getting it to work. As you would need to give
the WorkerClass the concept of "CurrentWorker", where "CurrentWorker" is a
shared property that returns the ThreadStatic variable, with the start
method (of the thread) the WorkerClass would need to save the instance in
the ThreadStatic variable...

Hope this helps
Jay


Charles Law said:
My first thought was to call

WorkerThread.Suspend

but the help cautions against this (for good reason) because the caller
has no control over where the thread actually stops, and it might have a
lock pending, for example.

I want to be able to stop a thread temporarily, and then optionally resume
it or stop it for good.

The code in the worker thread calls a recursive function, so passing some
flag down to the level at which the code is executing is quite messy. It
also calls methods on other objects that are defined locally, so a
top-level Pause method would not readily have a handle to one of the
secondary objects.

I should add that the worker thread code is contained in a worker thread
class, so what I am actually doing is something like

Dim wc as New WorkerClass

wc.StartThread ' starts worker thread and returns

' Do stuff
...

wc.Pause

It is this Pause that needs to stop the worker thread as soon as possible,
but at a sensible point.

Can anyone suggest a good technique for doing this? A pattern maybe?

A secondary question is: what should the thread do whilst it is paused?
Would it be reasonable for the thread to call Suspend on itself once it
has received the pause trigger? At least it would be able to execute the
suspend in a sensible place.

TIA

Charles
[I know ... you don't hear from me for ages and then suddenly the flood
gates open]
 
C

Charles Law

Jay

The worker thread is being paused by another thread.
Is the recursive function part of WorkerClass or part of the other
objects?

The recursive function is part of the other objects so, as you say, these
objects cannot readily check an instance variable.

Could you elaborate on the other two options? I am not quite sure how a
manual reset event would help where the recursive methods are concerned.
Would I still need to pass a reference into these recursive objects, and if
so, would that not also apply to the third option? Of course, if I am
passing in a reference, I can see how it would work, where a simple variable
would have to be passed at the point of pausing, rather than just at the
time of object creation.

Charles


Jay B. Harlow said:
Charles,
Is the worker thread going to pause itself or is another thread going to
pause the worker thread?

If another thread is going to pause the worker thread, my first choice
would be to use Thread.Suspend & Thread.Resume. However understanding the
caveat you identified.
It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.
I would make WorkerClass.Pause set an instance level variable, then at
opportune times the WorkerClass itself would call Thread.Suspend based on
this variable...

I would then make WorkerClass.UnPause check to see if the Worker Thread
itself is suspended & call Thread.Resume.

Is the recursive function part of WorkerClass or part of the other
objects? If its part of WorkerClass it can easily check a instance level
variable. As you mention the other objects won't be able to check this
variable.

An alternative to Thread.Suspend & Thread.Resume might be a
ManualResetEvent, where WorkerClass.Pause Resets the event, and
WorkerClass.UnPause sets the event, and at opportune times the WorkerClass
itself would WaitOne on the event. (thinking about it I might prefer the
ManualResetEvent over a different variable & Thread.Suspend &
Thread.Resume...).

A more advanced alternative might be using a shared field with
System.ThreadStaticAttribute in your WorkerClass you might be able to give
the other objects an opportune time to suspend the thread, however the
code might get rather involved on getting it to work. As you would need to
give the WorkerClass the concept of "CurrentWorker", where "CurrentWorker"
is a shared property that returns the ThreadStatic variable, with the
start method (of the thread) the WorkerClass would need to save the
instance in the ThreadStatic variable...

Hope this helps
Jay


Charles Law said:
My first thought was to call

WorkerThread.Suspend

but the help cautions against this (for good reason) because the caller
has no control over where the thread actually stops, and it might have a
lock pending, for example.

I want to be able to stop a thread temporarily, and then optionally
resume it or stop it for good.

The code in the worker thread calls a recursive function, so passing some
flag down to the level at which the code is executing is quite messy. It
also calls methods on other objects that are defined locally, so a
top-level Pause method would not readily have a handle to one of the
secondary objects.

I should add that the worker thread code is contained in a worker thread
class, so what I am actually doing is something like

Dim wc as New WorkerClass

wc.StartThread ' starts worker thread and returns

' Do stuff
...

wc.Pause

It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.

Can anyone suggest a good technique for doing this? A pattern maybe?

A secondary question is: what should the thread do whilst it is paused?
Would it be reasonable for the thread to call Suspend on itself once it
has received the pause trigger? At least it would be able to execute the
suspend in a sensible place.

TIA

Charles
[I know ... you don't hear from me for ages and then suddenly the flood
gates open]
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
Could you elaborate on the other two options? I am not quite sure how a
manual reset event would help where the recursive methods are concerned.
The manual reset event does not help the recursive methods directly, it
helps them indirectly as the first thread Resets the event when the worker
thread needs to suspend itself. While the first thread Sets the event when
the worker thread is allowed to run.

The worker thread simply needs to periodically wait on the event. If the
event is Set, the worker thread will be allowed to continue to run, if the
event is Reset, the worker thread will be suspended. If the worker thread is
suspended, when the first thread sets the event, the worker thread would
then "wake up" and continue to run...


As you stated the other objects still need to call back into the
WorkerThread class to check the event. You could do this either with a
parameter or a shared "CurrentWorker" property that relied on a ThreadStatic
field. It would really depend on what the other objects were doing if I
allowed them to have WorkerThread check the event, as this checking is
analogous to (very similar to) calling DoEvents in Windows Forms...
(technically its the inverse operation, however its analogous, as it gives
the code being called control over the threading)..


NOTE: The manual reset event does not have a concept of depth, its either on
or off...

Hope this helps
Jay


Charles Law said:
Jay

The worker thread is being paused by another thread.
Is the recursive function part of WorkerClass or part of the other
objects?

The recursive function is part of the other objects so, as you say, these
objects cannot readily check an instance variable.

Could you elaborate on the other two options? I am not quite sure how a
manual reset event would help where the recursive methods are concerned.
Would I still need to pass a reference into these recursive objects, and
if so, would that not also apply to the third option? Of course, if I am
passing in a reference, I can see how it would work, where a simple
variable would have to be passed at the point of pausing, rather than just
at the time of object creation.

Charles


Jay B. Harlow said:
Charles,
Is the worker thread going to pause itself or is another thread going to
pause the worker thread?

If another thread is going to pause the worker thread, my first choice
would be to use Thread.Suspend & Thread.Resume. However understanding the
caveat you identified.
It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.
I would make WorkerClass.Pause set an instance level variable, then at
opportune times the WorkerClass itself would call Thread.Suspend based on
this variable...

I would then make WorkerClass.UnPause check to see if the Worker Thread
itself is suspended & call Thread.Resume.

Is the recursive function part of WorkerClass or part of the other
objects? If its part of WorkerClass it can easily check a instance level
variable. As you mention the other objects won't be able to check this
variable.

An alternative to Thread.Suspend & Thread.Resume might be a
ManualResetEvent, where WorkerClass.Pause Resets the event, and
WorkerClass.UnPause sets the event, and at opportune times the
WorkerClass itself would WaitOne on the event. (thinking about it I might
prefer the ManualResetEvent over a different variable & Thread.Suspend &
Thread.Resume...).

A more advanced alternative might be using a shared field with
System.ThreadStaticAttribute in your WorkerClass you might be able to
give the other objects an opportune time to suspend the thread, however
the code might get rather involved on getting it to work. As you would
need to give the WorkerClass the concept of "CurrentWorker", where
"CurrentWorker" is a shared property that returns the ThreadStatic
variable, with the start method (of the thread) the WorkerClass would
need to save the instance in the ThreadStatic variable...

Hope this helps
Jay


Charles Law said:
My first thought was to call

WorkerThread.Suspend

but the help cautions against this (for good reason) because the caller
has no control over where the thread actually stops, and it might have a
lock pending, for example.

I want to be able to stop a thread temporarily, and then optionally
resume it or stop it for good.

The code in the worker thread calls a recursive function, so passing
some flag down to the level at which the code is executing is quite
messy. It also calls methods on other objects that are defined locally,
so a top-level Pause method would not readily have a handle to one of
the secondary objects.

I should add that the worker thread code is contained in a worker thread
class, so what I am actually doing is something like

Dim wc as New WorkerClass

wc.StartThread ' starts worker thread and returns

' Do stuff
...

wc.Pause

It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.

Can anyone suggest a good technique for doing this? A pattern maybe?

A secondary question is: what should the thread do whilst it is paused?
Would it be reasonable for the thread to call Suspend on itself once it
has received the pause trigger? At least it would be able to execute the
suspend in a sensible place.

TIA

Charles
[I know ... you don't hear from me for ages and then suddenly the flood
gates open]
 
C

Charles Law

Ok. I think I'm with you now. So, in the scenario below


UI | Worker Object1 Object2
start
-----> -----> -----> Do
<----- ...
Loop
<---------|
-----> ...
pause
------>


What I am trying to show is that the UI kicks off the worker and gets
control back immediately. The worker passes control to Object1, which passes
control to Object2. Object2 loops for a bit, doing stuff, including making
recursive calls. Then, control passes back to Object1. Later, control passes
back to Object2 to do more stuff, and so on.

Then, the UI pauses the worker thread. The worker action is all taking place
down in Object2, and maybe a couple of recursions deep. Am I right to say
that when the worker passes control to Object1, it has to pass a
reference/handle to the reset event. Object1 must pass this to Object2. So
long as the recursion remains within Object2, no further parameter need be
passed, as the handle can be held in an object level variable. If the
recursion ventures into another object then that object must receive a
reference to the event.

Object2 must occasionally wait on the event, most logically within the loop.

When the pause occurs, the event is reset by the worker, and no further
action need be taken, as this will be seen wherever the thread waits on the
event, even in other objects.

Where does the ThreadStatic field come in? As I understand it, such a field
would still only be visible to the object in which it was created, so it
does not eliminate the need for passing a reference to the other objects in
the processing chain. Is there a way to prevent the need for passing a
reference around?

Charles


Jay B. Harlow said:
Charles,
Could you elaborate on the other two options? I am not quite sure how a
manual reset event would help where the recursive methods are concerned.
The manual reset event does not help the recursive methods directly, it
helps them indirectly as the first thread Resets the event when the worker
thread needs to suspend itself. While the first thread Sets the event when
the worker thread is allowed to run.

The worker thread simply needs to periodically wait on the event. If the
event is Set, the worker thread will be allowed to continue to run, if the
event is Reset, the worker thread will be suspended. If the worker thread
is suspended, when the first thread sets the event, the worker thread
would then "wake up" and continue to run...


As you stated the other objects still need to call back into the
WorkerThread class to check the event. You could do this either with a
parameter or a shared "CurrentWorker" property that relied on a
ThreadStatic field. It would really depend on what the other objects were
doing if I allowed them to have WorkerThread check the event, as this
checking is analogous to (very similar to) calling DoEvents in Windows
Forms... (technically its the inverse operation, however its analogous, as
it gives the code being called control over the threading)..


NOTE: The manual reset event does not have a concept of depth, its either
on or off...

Hope this helps
Jay


Charles Law said:
Jay

The worker thread is being paused by another thread.
Is the recursive function part of WorkerClass or part of the other
objects?

The recursive function is part of the other objects so, as you say, these
objects cannot readily check an instance variable.

Could you elaborate on the other two options? I am not quite sure how a
manual reset event would help where the recursive methods are concerned.
Would I still need to pass a reference into these recursive objects, and
if so, would that not also apply to the third option? Of course, if I am
passing in a reference, I can see how it would work, where a simple
variable would have to be passed at the point of pausing, rather than
just at the time of object creation.

Charles


Jay B. Harlow said:
Charles,
Is the worker thread going to pause itself or is another thread going to
pause the worker thread?

If another thread is going to pause the worker thread, my first choice
would be to use Thread.Suspend & Thread.Resume. However understanding
the caveat you identified.

It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.
I would make WorkerClass.Pause set an instance level variable, then at
opportune times the WorkerClass itself would call Thread.Suspend based
on this variable...

I would then make WorkerClass.UnPause check to see if the Worker Thread
itself is suspended & call Thread.Resume.

Is the recursive function part of WorkerClass or part of the other
objects? If its part of WorkerClass it can easily check a instance level
variable. As you mention the other objects won't be able to check this
variable.

An alternative to Thread.Suspend & Thread.Resume might be a
ManualResetEvent, where WorkerClass.Pause Resets the event, and
WorkerClass.UnPause sets the event, and at opportune times the
WorkerClass itself would WaitOne on the event. (thinking about it I
might prefer the ManualResetEvent over a different variable &
Thread.Suspend & Thread.Resume...).

A more advanced alternative might be using a shared field with
System.ThreadStaticAttribute in your WorkerClass you might be able to
give the other objects an opportune time to suspend the thread, however
the code might get rather involved on getting it to work. As you would
need to give the WorkerClass the concept of "CurrentWorker", where
"CurrentWorker" is a shared property that returns the ThreadStatic
variable, with the start method (of the thread) the WorkerClass would
need to save the instance in the ThreadStatic variable...

Hope this helps
Jay


My first thought was to call

WorkerThread.Suspend

but the help cautions against this (for good reason) because the caller
has no control over where the thread actually stops, and it might have
a lock pending, for example.

I want to be able to stop a thread temporarily, and then optionally
resume it or stop it for good.

The code in the worker thread calls a recursive function, so passing
some flag down to the level at which the code is executing is quite
messy. It also calls methods on other objects that are defined locally,
so a top-level Pause method would not readily have a handle to one of
the secondary objects.

I should add that the worker thread code is contained in a worker
thread class, so what I am actually doing is something like

Dim wc as New WorkerClass

wc.StartThread ' starts worker thread and returns

' Do stuff
...

wc.Pause

It is this Pause that needs to stop the worker thread as soon as
possible, but at a sensible point.

Can anyone suggest a good technique for doing this? A pattern maybe?

A secondary question is: what should the thread do whilst it is paused?
Would it be reasonable for the thread to call Suspend on itself once it
has received the pause trigger? At least it would be able to execute
the suspend in a sensible place.

TIA

Charles
[I know ... you don't hear from me for ages and then suddenly the flood
gates open]
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
The rules of encapsulation states, that you do not pass the Event itself to
object1 & object2, as the Event itself is an implementation detail of the
Worker class.

You could pass the instance of the Worker class to object1 & object2,
however it can be easier for object1 & object2 to simply call a shared
method of the Worker class. Either way object1 & object2 would be coupled to
Worker, whether I used a shared property or a parameter would depend on how
many routines & classes I needed to pass the parameter to. If object1 &
object2 "exists" only on a single thread I have been known to pass the
"Worker" class to the constructor of object1 & object2, then the any method
can gain access to the "current" "Worker" object...


If you call a shared method on the Worker class, the Worker class needs to
know the instance of the Worker class that is associated with the current
thread. You can use the Thread Static variable to associate the instance of
the Worker class with each thread, as the ThreadStatic variable causes the
shared member to be unique per thread... Alternatively you could use a
shared HashTable, where the Thread ID is the key & the Worker instance is
the value...


By encapsulating the Event within Worker you also gain more readable code,
your UI thread can call Worker.Suspend & Worker.Resume, while the Worker
thread itself would call Worker.CheckSuspend.


Here is a an example of a Worker class using a ManualResetEvent to control
suspending & resuming.

Public Class Worker

Public Delegate Sub Work()

Private ReadOnly m_work As Work
Private ReadOnly m_arg As Object
Private ReadOnly m_event As ManualResetEvent
Private ReadOnly m_thread As Thread

<ThreadStatic()> _
Private Shared m_current As Worker

Public Sub New(ByVal work As Work, ByVal arg As Object, ByVal name
As String)
m_work = work
m_arg = arg
m_event = New ManualResetEvent(True)
m_thread = New Thread(AddressOf Start)
m_thread.Name = name
m_thread.IsBackground = True
m_thread.Start()
End Sub

Private Sub Start()
m_current = Me
m_work.Invoke()
End Sub

Public Shared ReadOnly Property CurrentWorker() As Worker
Get
Return m_current
End Get
End Property

Public ReadOnly Property Arg() As Object
Get
Return m_arg
End Get
End Property

Public ReadOnly Property Name() As String
Get
Return m_thread.Name
End Get
End Property

Public Sub Suspend()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Suspend should not be
called from the worker thread!")
End If
m_event.Reset()
End Sub

Public Sub [Resume]()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Resume should not be
called from the worker thread!")
End If
m_event.Set()
End Sub

Public Sub WaitForTermination()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("WaitForTermination
should not be called from the worker thread!")
End If
m_thread.Join()
End Sub

Public Sub CheckSuspend()
If Not Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("CheckSuspend should
only be called from the worker thread!")
End If
m_event.WaitOne()
End Sub

End Class



To see how it works try something like:

Private Sub Work()
For index As Integer = 1 To 50
Worker.CurrentWorker.CheckSuspend()
Debug.WriteLine(index, Worker.CurrentWorker.Name)
Dim value As Double = DirectCast(Worker.CurrentWorker.Arg,
Double)
Thread.Sleep(TimeSpan.FromSeconds(value))
Next
End Sub

Public Sub Main()
Dim worker1 As New Worker(AddressOf Work, 0.25, "worker1")
Dim worker2 As New Worker(AddressOf Work, 0.5, "worker2")
Dim worker3 As New Worker(AddressOf Work, 0.75, "worker3")

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker1.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker2.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker3.Suspend()

Debug.WriteLine("Pausing 1 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(1))

worker1.Resume()
worker2.Resume()
worker3.Resume()

Debug.WriteLine("Waiting for Termination", "Main")
worker1.WaitForTermination()
worker2.WaitForTermination()
worker3.WaitForTermination()

End Sub

Hope this helps
Jay

Charles Law said:
Ok. I think I'm with you now. So, in the scenario below


UI | Worker Object1 Object2
start
-----> -----> -----> Do
<----- ...
Loop
<---------|
-----> ...
pause
------>


What I am trying to show is that the UI kicks off the worker and gets
control back immediately. The worker passes control to Object1, which
passes control to Object2. Object2 loops for a bit, doing stuff, including
making recursive calls. Then, control passes back to Object1. Later,
control passes back to Object2 to do more stuff, and so on.

Then, the UI pauses the worker thread. The worker action is all taking
place down in Object2, and maybe a couple of recursions deep. Am I right
to say that when the worker passes control to Object1, it has to pass a
reference/handle to the reset event. Object1 must pass this to Object2. So
long as the recursion remains within Object2, no further parameter need be
passed, as the handle can be held in an object level variable. If the
recursion ventures into another object then that object must receive a
reference to the event.

Object2 must occasionally wait on the event, most logically within the
loop.

When the pause occurs, the event is reset by the worker, and no further
action need be taken, as this will be seen wherever the thread waits on
the event, even in other objects.

Where does the ThreadStatic field come in? As I understand it, such a
field would still only be visible to the object in which it was created,
so it does not eliminate the need for passing a reference to the other
objects in the processing chain. Is there a way to prevent the need for
passing a reference around?

Charles
<<snip>>
 
J

Jay B. Harlow [MVP - Outlook]

An alternate Worker.Suspend implementation might be:

Public Sub Suspend()
m_event.Reset()
If Thread.CurrentThread Is m_thread Then
m_event.WaitOne()
End If
End Sub

Which allows the worker thread to suspend itself.

I would also consider implementing CurrentWorker as:

Public Shared ReadOnly Property CurrentWorker() As Worker
Get
If m_current Is Nothing Then
Throw New InvalidOperationException("CurrentWorker
should only be called from a Worker thread!")
End If
Return m_current
End Get
End Property

To ensure that it is only called from Worker Threads (preventing
NullReferenceExceptions in non-worker threads!).

Hope this helps
Jay

Jay B. Harlow said:
Charles,
The rules of encapsulation states, that you do not pass the Event itself
to object1 & object2, as the Event itself is an implementation detail of
the Worker class.

You could pass the instance of the Worker class to object1 & object2,
however it can be easier for object1 & object2 to simply call a shared
method of the Worker class. Either way object1 & object2 would be coupled
to Worker, whether I used a shared property or a parameter would depend on
how many routines & classes I needed to pass the parameter to. If object1
& object2 "exists" only on a single thread I have been known to pass the
"Worker" class to the constructor of object1 & object2, then the any
method can gain access to the "current" "Worker" object...


If you call a shared method on the Worker class, the Worker class needs to
know the instance of the Worker class that is associated with the current
thread. You can use the Thread Static variable to associate the instance
of the Worker class with each thread, as the ThreadStatic variable causes
the shared member to be unique per thread... Alternatively you could use a
shared HashTable, where the Thread ID is the key & the Worker instance is
the value...


By encapsulating the Event within Worker you also gain more readable code,
your UI thread can call Worker.Suspend & Worker.Resume, while the Worker
thread itself would call Worker.CheckSuspend.


Here is a an example of a Worker class using a ManualResetEvent to control
suspending & resuming.

Public Class Worker

Public Delegate Sub Work()

Private ReadOnly m_work As Work
Private ReadOnly m_arg As Object
Private ReadOnly m_event As ManualResetEvent
Private ReadOnly m_thread As Thread

<ThreadStatic()> _
Private Shared m_current As Worker

Public Sub New(ByVal work As Work, ByVal arg As Object, ByVal name
As String)
m_work = work
m_arg = arg
m_event = New ManualResetEvent(True)
m_thread = New Thread(AddressOf Start)
m_thread.Name = name
m_thread.IsBackground = True
m_thread.Start()
End Sub

Private Sub Start()
m_current = Me
m_work.Invoke()
End Sub

Public Shared ReadOnly Property CurrentWorker() As Worker
Get
Return m_current
End Get
End Property

Public ReadOnly Property Arg() As Object
Get
Return m_arg
End Get
End Property

Public ReadOnly Property Name() As String
Get
Return m_thread.Name
End Get
End Property

Public Sub Suspend()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Suspend should not be
called from the worker thread!")
End If
m_event.Reset()
End Sub

Public Sub [Resume]()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Resume should not be
called from the worker thread!")
End If
m_event.Set()
End Sub

Public Sub WaitForTermination()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("WaitForTermination
should not be called from the worker thread!")
End If
m_thread.Join()
End Sub

Public Sub CheckSuspend()
If Not Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("CheckSuspend should
only be called from the worker thread!")
End If
m_event.WaitOne()
End Sub

End Class



To see how it works try something like:

Private Sub Work()
For index As Integer = 1 To 50
Worker.CurrentWorker.CheckSuspend()
Debug.WriteLine(index, Worker.CurrentWorker.Name)
Dim value As Double = DirectCast(Worker.CurrentWorker.Arg,
Double)
Thread.Sleep(TimeSpan.FromSeconds(value))
Next
End Sub

Public Sub Main()
Dim worker1 As New Worker(AddressOf Work, 0.25, "worker1")
Dim worker2 As New Worker(AddressOf Work, 0.5, "worker2")
Dim worker3 As New Worker(AddressOf Work, 0.75, "worker3")

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker1.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker2.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker3.Suspend()

Debug.WriteLine("Pausing 1 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(1))

worker1.Resume()
worker2.Resume()
worker3.Resume()

Debug.WriteLine("Waiting for Termination", "Main")
worker1.WaitForTermination()
worker2.WaitForTermination()
worker3.WaitForTermination()

End Sub

Hope this helps
Jay

Charles Law said:
Ok. I think I'm with you now. So, in the scenario below


UI | Worker Object1 Object2
start
-----> -----> -----> Do
<----- ...
Loop
<---------|
-----> ...
pause
------>


What I am trying to show is that the UI kicks off the worker and gets
control back immediately. The worker passes control to Object1, which
passes control to Object2. Object2 loops for a bit, doing stuff,
including making recursive calls. Then, control passes back to Object1.
Later, control passes back to Object2 to do more stuff, and so on.

Then, the UI pauses the worker thread. The worker action is all taking
place down in Object2, and maybe a couple of recursions deep. Am I right
to say that when the worker passes control to Object1, it has to pass a
reference/handle to the reset event. Object1 must pass this to Object2.
So long as the recursion remains within Object2, no further parameter
need be passed, as the handle can be held in an object level variable. If
the recursion ventures into another object then that object must receive
a reference to the event.

Object2 must occasionally wait on the event, most logically within the
loop.

When the pause occurs, the event is reset by the worker, and no further
action need be taken, as this will be seen wherever the thread waits on
the event, even in other objects.

Where does the ThreadStatic field come in? As I understand it, such a
field would still only be visible to the object in which it was created,
so it does not eliminate the need for passing a reference to the other
objects in the processing chain. Is there a way to prevent the need for
passing a reference around?

Charles
<<snip>>
 
C

Charles Law

Hi Jay

I have spent some time digesting this, and have now implemented something
along these lines in my project. It works just fine.

Thanks.

Charles


Jay B. Harlow said:
Charles,
The rules of encapsulation states, that you do not pass the Event itself
to object1 & object2, as the Event itself is an implementation detail of
the Worker class.

You could pass the instance of the Worker class to object1 & object2,
however it can be easier for object1 & object2 to simply call a shared
method of the Worker class. Either way object1 & object2 would be coupled
to Worker, whether I used a shared property or a parameter would depend on
how many routines & classes I needed to pass the parameter to. If object1
& object2 "exists" only on a single thread I have been known to pass the
"Worker" class to the constructor of object1 & object2, then the any
method can gain access to the "current" "Worker" object...


If you call a shared method on the Worker class, the Worker class needs to
know the instance of the Worker class that is associated with the current
thread. You can use the Thread Static variable to associate the instance
of the Worker class with each thread, as the ThreadStatic variable causes
the shared member to be unique per thread... Alternatively you could use a
shared HashTable, where the Thread ID is the key & the Worker instance is
the value...


By encapsulating the Event within Worker you also gain more readable code,
your UI thread can call Worker.Suspend & Worker.Resume, while the Worker
thread itself would call Worker.CheckSuspend.


Here is a an example of a Worker class using a ManualResetEvent to control
suspending & resuming.

Public Class Worker

Public Delegate Sub Work()

Private ReadOnly m_work As Work
Private ReadOnly m_arg As Object
Private ReadOnly m_event As ManualResetEvent
Private ReadOnly m_thread As Thread

<ThreadStatic()> _
Private Shared m_current As Worker

Public Sub New(ByVal work As Work, ByVal arg As Object, ByVal name
As String)
m_work = work
m_arg = arg
m_event = New ManualResetEvent(True)
m_thread = New Thread(AddressOf Start)
m_thread.Name = name
m_thread.IsBackground = True
m_thread.Start()
End Sub

Private Sub Start()
m_current = Me
m_work.Invoke()
End Sub

Public Shared ReadOnly Property CurrentWorker() As Worker
Get
Return m_current
End Get
End Property

Public ReadOnly Property Arg() As Object
Get
Return m_arg
End Get
End Property

Public ReadOnly Property Name() As String
Get
Return m_thread.Name
End Get
End Property

Public Sub Suspend()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Suspend should not be
called from the worker thread!")
End If
m_event.Reset()
End Sub

Public Sub [Resume]()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("Resume should not be
called from the worker thread!")
End If
m_event.Set()
End Sub

Public Sub WaitForTermination()
If Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("WaitForTermination
should not be called from the worker thread!")
End If
m_thread.Join()
End Sub

Public Sub CheckSuspend()
If Not Thread.CurrentThread Is m_thread Then
Throw New InvalidOperationException("CheckSuspend should
only be called from the worker thread!")
End If
m_event.WaitOne()
End Sub

End Class



To see how it works try something like:

Private Sub Work()
For index As Integer = 1 To 50
Worker.CurrentWorker.CheckSuspend()
Debug.WriteLine(index, Worker.CurrentWorker.Name)
Dim value As Double = DirectCast(Worker.CurrentWorker.Arg,
Double)
Thread.Sleep(TimeSpan.FromSeconds(value))
Next
End Sub

Public Sub Main()
Dim worker1 As New Worker(AddressOf Work, 0.25, "worker1")
Dim worker2 As New Worker(AddressOf Work, 0.5, "worker2")
Dim worker3 As New Worker(AddressOf Work, 0.75, "worker3")

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker1.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker2.Suspend()

Debug.WriteLine("Pausing 5 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(5))
worker3.Suspend()

Debug.WriteLine("Pausing 1 seconds", "Main")
Thread.Sleep(TimeSpan.FromSeconds(1))

worker1.Resume()
worker2.Resume()
worker3.Resume()

Debug.WriteLine("Waiting for Termination", "Main")
worker1.WaitForTermination()
worker2.WaitForTermination()
worker3.WaitForTermination()

End Sub

Hope this helps
Jay

Charles Law said:
Ok. I think I'm with you now. So, in the scenario below


UI | Worker Object1 Object2
start
-----> -----> -----> Do
<----- ...
Loop
<---------|
-----> ...
pause
------>


What I am trying to show is that the UI kicks off the worker and gets
control back immediately. The worker passes control to Object1, which
passes control to Object2. Object2 loops for a bit, doing stuff,
including making recursive calls. Then, control passes back to Object1.
Later, control passes back to Object2 to do more stuff, and so on.

Then, the UI pauses the worker thread. The worker action is all taking
place down in Object2, and maybe a couple of recursions deep. Am I right
to say that when the worker passes control to Object1, it has to pass a
reference/handle to the reset event. Object1 must pass this to Object2.
So long as the recursion remains within Object2, no further parameter
need be passed, as the handle can be held in an object level variable. If
the recursion ventures into another object then that object must receive
a reference to the event.

Object2 must occasionally wait on the event, most logically within the
loop.

When the pause occurs, the event is reset by the worker, and no further
action need be taken, as this will be seen wherever the thread waits on
the event, even in other objects.

Where does the ThreadStatic field come in? As I understand it, such a
field would still only be visible to the object in which it was created,
so it does not eliminate the need for passing a reference to the other
objects in the processing chain. Is there a way to prevent the need for
passing a reference around?

Charles
<<snip>>
 

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