Canceling a background thread

  • Thread starter Thread starter JohnnyGr
  • Start date Start date
J

JohnnyGr

Im trying to figure out how i can cancel the currently running thread when
the lwFiles_ItemSelectionChanged sub is triggered... the purpose of the
threading
is to load a thumbnail for a image, these images can be pretty large but i
still
want to be able to change image in my file list and start loading a new
image instead

this works now but not that good really, since the old thread is running in
the background
until it hits the If Not CurrentImage = filename Then... clause...

does anyone have any idea on how i can kill the old thread before invoking
the new one?

Im using framework 2.0


I have the following code so far...


Private ad As New GetImageDataDelegate(AddressOf
GetImageExifAndThumbnail)
Delegate Sub GetImageDataDelegate(ByVal filename As String)
Delegate Sub UpdateThumbnailAndExifDelegate(ByRef thumbnail As Image,
ByRef exifdata As Hashtable)
Dim CurrentImage As String


'<Summary>
' Triggers when a image is selected in the file list
' and gets the exif information and thumbnail
'</Summary>
Private Sub lwFiles_ItemSelectionChanged(ByVal sender As Object, ByVal e
As System.Windows.Forms.ListViewItemSelectionChangedEventArgs) Handles
lwFiles.ItemSelectionChanged
If e.IsSelected Then

Try
Dim CallBack As New AsyncCallback(AddressOf
GetImageExifAndThumbnailCallBack)
Dim ia As IAsyncResult = ad.BeginInvoke(e.Item.Tag,
CallBack, ad)
Catch ex As Exception

End Try

End If
End Sub

Private Sub UpdateThumbnailAndExif(ByRef thumbnail As Image, ByRef
exifdata As Hashtable)
Debug.WriteLine(thumbnail.Width)

If Me.picPreview.InvokeRequired Then
Dim d As New UpdateThumbnailAndExifDelegate(AddressOf
UpdateThumbnailAndExif)
Me.Invoke(d, New Object() {thumbnail, exifdata})
Else
Me.picPreview.Image = thumbnail
End If

End Sub

Private Sub GetImageExifAndThumbnail(ByVal filename As String)

CurrentImage = filename

Debug.WriteLine(filename)

Dim hash As Hashtable = Nothing

Dim image As New Bitmap(filename, True)

If Not CurrentImage = filename Then
image.Dispose()
Debug.Write("Exiting thread")
Exit Sub
End If

Dim thumbnail As Image

thumbnail = image.GetThumbnailImage(100, 100, Nothing, Nothing)

image.Dispose()

If CurrentImage = filename Then
Dim uidel As New UpdateThumbnailAndExifDelegate(AddressOf
UpdateThumbnailAndExif)
uidel.Invoke(thumbnail, hash)
Else
image.Dispose()
hash.Clear()
End If

End Sub

Private Sub GetImageExifAndThumbnailCallBack(ByVal ia As IAsyncResult)
Debug.WriteLine("callback")
CType(ia.AsyncState, GetImageDataDelegate).EndInvoke(ia)
End Sub
 
JohnnyGr said:
Im trying to figure out how i can cancel the currently running thread when
the lwFiles_ItemSelectionChanged sub is triggered... the purpose of the
threading
is to load a thumbnail for a image, these images can be pretty large but i
still
want to be able to change image in my file list and start loading a new
image instead

this works now but not that good really, since the old thread is running in
the background
until it hits the If Not CurrentImage = filename Then... clause...

does anyone have any idea on how i can kill the old thread before invoking
the new one?

Im using framework 2.0


I have the following code so far...


Private ad As New GetImageDataDelegate(AddressOf
GetImageExifAndThumbnail)
Delegate Sub GetImageDataDelegate(ByVal filename As String)
Delegate Sub UpdateThumbnailAndExifDelegate(ByRef thumbnail As Image,
ByRef exifdata As Hashtable)
Dim CurrentImage As String


'<Summary>
' Triggers when a image is selected in the file list
' and gets the exif information and thumbnail
'</Summary>
Private Sub lwFiles_ItemSelectionChanged(ByVal sender As Object, ByVal e
As System.Windows.Forms.ListViewItemSelectionChangedEventArgs) Handles
lwFiles.ItemSelectionChanged
If e.IsSelected Then

Try
Dim CallBack As New AsyncCallback(AddressOf
GetImageExifAndThumbnailCallBack)
Dim ia As IAsyncResult = ad.BeginInvoke(e.Item.Tag,
CallBack, ad)
Catch ex As Exception

End Try

End If
End Sub

Private Sub UpdateThumbnailAndExif(ByRef thumbnail As Image, ByRef
exifdata As Hashtable)
Debug.WriteLine(thumbnail.Width)

If Me.picPreview.InvokeRequired Then
Dim d As New UpdateThumbnailAndExifDelegate(AddressOf
UpdateThumbnailAndExif)
Me.Invoke(d, New Object() {thumbnail, exifdata})
Else
Me.picPreview.Image = thumbnail
End If

End Sub

Private Sub GetImageExifAndThumbnail(ByVal filename As String)

CurrentImage = filename

Debug.WriteLine(filename)

Dim hash As Hashtable = Nothing

Dim image As New Bitmap(filename, True)

If Not CurrentImage = filename Then
image.Dispose()
Debug.Write("Exiting thread")
Exit Sub
End If

Dim thumbnail As Image

thumbnail = image.GetThumbnailImage(100, 100, Nothing, Nothing)

image.Dispose()

If CurrentImage = filename Then
Dim uidel As New UpdateThumbnailAndExifDelegate(AddressOf
UpdateThumbnailAndExif)
uidel.Invoke(thumbnail, hash)
Else
image.Dispose()
hash.Clear()
End If

End Sub

Private Sub GetImageExifAndThumbnailCallBack(ByVal ia As IAsyncResult)
Debug.WriteLine("callback")
CType(ia.AsyncState, GetImageDataDelegate).EndInvoke(ia)
End Sub

If I understand the question, I think you'll need some kind of global
resource for synchronization, I would look into using a mutex, monitor,
or semaphore to manage your threads.
 
Well i dont really want to manage it... i just want to kill it, like no
mercy kill it...

since the thread loads a image and i have a list of images, and as i said if
a user clicks a image
then it will be loaded in a thread... and if the image is big it continues
to load into the bitmap object even if the user
clicks another image, wich in turn creates another thread....

so in theory i can get like infinite numbers of threads that takes up a
total of 100% cpu since all threads are loading a image
and wich will probably kill the application sooner or later...
 
JohnnyGr said:
Well i dont really want to manage it... i just want to kill it, like
no mercy kill it...

You should not do this. Your thread procedure should perform some loop
with a flag that can be step by other threads to indicate if the
procedure should stop. Then you should do a clean exit of the thread
procedure.

Your code is overly complicated. Here's a basic analysis:

This is a Forms event handler, so it runs on the GUI thread

Here you are using a ThreadPool thread to perform an async call. You
indicate that GetImageExifAndThumbnail is run on the ThreadPool threa,
and when it has finished the ThreadPool thread should call
GetImageExifAndThumbnailCallBack, to perform cleanup.


This is called on the ThreadPool thread and does the work.


You're calling UpdateThumbnailAndExif on the current thread. (You've
called the delegate synchronously). What is the point?


This is running on the ThreadPool thread.

Now you intend to make some code run on the GUI thread. This should be
stuff that accesses the UI. But this cannot be right. You are creating a
delegate to *this* method and then invoking it on the GUI thread. What
is the point?

Finally this will cleanup the async result object.

You code seems to be a bit of a mess. You need to make sure that your
processing occurs on the ThreadPool thread and not on the GUI thread.
You have done that by making sure that GetImageExifAndThumbnail runs on
a ThreadPool thread by calling it asynchronously. Then you call
UpdateThumbnailAndExif synchronously on the ThreadPool thread (your use
of delegates here is pointless, you could have just called
UpdateThumbnailAndExif directly). You should not do this, you should
call it on the GUI thread. That way you can simplify
UpdateThumbnailAndExif so that it simply updates the image preview.

Now, you have to decide where the cancelling can occur. Presumably the
action that takes the most time is the call to GetThumbnailImage. If you
have a 'Cancel' button you could make the Click handler for this button
set a instance variable to true (it is normally false) then before the
call to GetThumbnailImage test the variable to see if it is true and if
so return.

GetThumbnailImage takes a delegate, of type
Image.GetThumbnailImageAbort. Presumably GetThumbnailImage will
periodically call this delegate to see if the operation should abort.
Unfortunately, the documentation says that this is not used in GDI+ 1.0.
Thus you should treat GetThumbnailImage as an atomic operation and you
either call it or you don't.

The alternative (implied in your question) is to terminate the thread.
You should never terminate a thread since it could screw up the state of
your object.

Richard
 
Richard,
Did you give up your MVP-ship? Reasons?

--
Regards,
Alvin Bruney [MVP ASP.NET]

[Shameless Author plug]
The Microsoft Office Web Components Black Book with .NET
Now Available @ www.lulu.com/owc
Forth-coming VSTO.NET - Wrox/Wiley 2006
 
I ended upp removing all of this code and redoing it in another way...
and ended up with another problem i did not find in my initial attempt...

The function GetImageExifAndThumbnail opens a image, wich can be large, like
24mb+
and while the bitmap object is loading i cannot cancel it, and that was what
i wanted to cancel...
so even if i kill the thread the bitmap object is still loading and then
doesnt clean itself up...

so i made a custom filestream object wich i use to feed the bitmap object...
and by killing that filestream
i hoped everything would work out... wich it didnt, since the bitmap object
seem to have somekind of timeout...

so it doesnt really matter how i do this, if someone doesnt know how to make
a clean cancel of the bitmap objects
image loading ill still have the same problem...

Thank you for your helpful information, it will be useful in the future
anyway

Regards
johnny
 
Back
Top