.NET Forum / Languages / VB.NET / October 2004
Why Does This Fail ( Threading )
|
|
Thread rating:  |
One Handed Man \( OHM - Terry Burns \) - 21 Oct 2004 17:15 GMT Assumes a Form with a Panel on it., Does the Mutex have to be within the address of a thread start address ?
Cheers - OHM
'----------- *************** ----------------
Private endProgram As Boolean = False
Dim image1 As Image = Image.FromFile("..\Images\gun.bmp")
Dim image2 As Image = Image.FromFile("..\Images\Invader.bmp")
Dim space As Graphics
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Thread1 As New Threading.Thread(AddressOf Me.WriteImage1)
Dim Thread2 As New Threading.Thread(AddressOf Me.WriteImage2)
space = Panel1.CreateGraphics()
Thread1.Start()
Thread2.Start()
End Sub
Private Sub WriteImage1()
While Not endProgram
DrawImage(space, image1, New Point(10, 10))
End While
End Sub
Private Sub WriteImage2()
While Not endProgram
DrawImage(space, image2, New Point(50, 50))
End While
End Sub
Public Sub DrawImage(ByVal g As Graphics, ByVal i As Image, ByVal p As Point)
Dim m As New Threading.Mutex
m.WaitOne()
g.DrawImage(i, p)
m.ReleaseMutex()
End Sub
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
Larry Serflaten - 21 Oct 2004 18:55 GMT "One Handed Man ( OHM - Terry Burns )" <news.microsoft.com> wrote
> Assumes a Form with a Panel on it., Does the Mutex have to be within the > address of a thread start address ? I would think it would have to be declared at a larger scope than the procedure you need it in. I looks to me like you create and destroy a new mutex for each call....
LFS
> Public Sub DrawImage(ByVal g As Graphics, ByVal i As Image, ByVal p As > Point) [quoted text clipped - 8 lines] > > End Sub One Handed Man \( OHM - Terry Burns \) - 21 Oct 2004 21:59 GMT Tried decaling the Mutex in a module,that didnt work either
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> "One Handed Man ( OHM - Terry Burns )" <news.microsoft.com> wrote >> Assumes a Form with a Panel on it., Does the Mutex have to be within the [quoted text clipped - 18 lines] >> >> End Sub Larry Serflaten - 22 Oct 2004 09:57 GMT "One Handed Man ( OHM - Terry Burns )" <news.microsoft.com> wrote
> Tried decaling the Mutex in a module,that didnt work either I suggested using a delegate. Try this out in a new form with one button. Paste in the code after the designer code.
Also as I was building this up, it occured to me that some sort of pooling should be used, should it not? Invaders come and go over the course of the game, so pooling may let the system managed them a little better....
HTH LFS
Private GrxForm As Graphics Private th1, th2 As Threading.Thread Private brh As SolidBrush = New SolidBrush(Color.Black) Private rnd As Random = New Random Private fnt As Font = New Font("tahoma", 24, FontStyle.Bold)
Delegate Sub Drawing(ByVal ID As Integer, ByVal Loc As PointF) Private Dlg As New Drawing(AddressOf CommonDraw)
Private Done As Boolean
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click GrxForm = Me.CreateGraphics Th1 = New Threading.Thread(AddressOf Thread1) Th2 = New Threading.Thread(AddressOf Thread2) Th1.Start() Th2.Start() End Sub
Private Sub CommonDraw(ByVal ID As Integer, ByVal Loc As PointF) Dim klr As Color = Color.FromArgb(200, rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256)) ' The Brush, Font and Graphics are shared brh.Color = klr GrxForm.FillRectangle(brh, Loc.X, Loc.Y, 80, 50) GrxForm.DrawString(CStr(ID), fnt, Brushes.Black, Loc) End Sub
Sub Thread1() Dim id As Integer = 1 Dim loc As PointF = New PointF(10, 10) Do While Not Done Me.Invoke(Dlg, New Object() {id, loc}) Threading.Thread.Sleep(100) Loop End Sub
Sub Thread2() Dim id As Integer = 2 Dim loc As PointF = New PointF(100, 10) Do While Not Done Me.Invoke(Dlg, New Object() {id, loc}) Threading.Thread.Sleep(100) Loop End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing e.Cancel = Done Done = True Application.DoEvents() Th1.Abort() Th2.Abort() End Sub
Larry Serflaten - 22 Oct 2004 10:02 GMT > Private Sub Form1_Closing(...) > e.Cancel = Done [quoted text clipped - 3 lines] > Th2.Abort() > End Sub Should have been:
e.Cancel = Not Done
It was meant to cause the first hit on the close button to stop the threads (they take a while to close down) and the second hit to close the form....
(Rather than add another button to stop the threads...)
LFS
One Handed Man \( OHM - Terry Burns \) - 22 Oct 2004 12:54 GMT Thanks Larry,
I've tried your code out and it seems fine, I'll see if I can work it in to my code and observe the results. Threading is not something Ive had to do much of, so these kinds of issues have not arisen for me. It just goes to show that real work problems provide the best excercies to promote learning.
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
>> Private Sub Form1_Closing(...) >> e.Cancel = Done [quoted text clipped - 15 lines] > > LFS One Handed Man \( OHM - Terry Burns \) - 22 Oct 2004 17:24 GMT Actually, thinking about this more before I try and implement it.
My Invader and Gun classes are responsible for doing their own drawing, in this case they both have to use the/a delegate in which case perhaps I need to raise an event as they do not have an Invoke method as a form does.
Sorry, but I feel really feel like I cant think my way out of a paper bag today :(
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
>> Private Sub Form1_Closing(...) >> e.Cancel = Done [quoted text clipped - 15 lines] > > LFS One Handed Man \( OHM - Terry Burns \) - 23 Oct 2004 11:13 GMT Hi Larry,
I have implemented your solution in my code and it works. However, the way I had to do this was to pass a reference of the form to the Class Invader and Gun, this way I could do the Invoke of the method on the UI Thread in the form.
Is this the way you would have done it ?
Thanks
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
>> Private Sub Form1_Closing(...) >> e.Cancel = Done [quoted text clipped - 15 lines] > > LFS Larry Serflaten - 23 Oct 2004 13:26 GMT "One Handed Man ( OHM - Terry Burns )" <news.microsoft.com> wrote
> I have implemented your solution in my code and it works. However, the way I > had to do this was to pass a reference of the form to the Class Invader and > Gun, this way I could do the Invoke of the method on the UI Thread in the > form. > > Is this the way you would have done it ? You are using a design I would have avoided. If you are trying to duplicate 'Space Invaders' then you'd have rows of aliens that march side to side as they advance toward the player.
Because the whole row advances, and moves as a unit, I would have tried to make a class that handles the whole row. In addition to fewer objects, I'd be copying an image of the whole row over at a time, and not each individual invader.
But, with that said, it may be this is more of an exercise in using threads, which is OK, if that is what you are really after....
At first glance, it seems you should be using a collection (of some sort) to house all the invaders that are currently being used. I would think the call from the invaders should be made to their container, to interact with the outside world. That would mean the invaders call on a method of their collection object, who forwards their call to the form. That forwarding routine should add a check to be sure the form reference is still valid before forwarding the call.
While that may be a more refined approach, it may just be more practical to make the form available globally (project wide) and let the invaders call it direct.
Because that second method would be easy to code and understand, that is probably what I would do. A few choice (global) references is sometimes necessary to simplify a large part of the program. This certainly seems to be one of those times....
While some may discourage using global variables, and avoid them at all costs, I am not that strict. It pays off, on occasion, to make a few objects globally available. To those who think otherwise, I would simply ask them how they would call DoEvents, if the Application object wasn't available, or other such common tasks that are included in (what appears to be) globally available objects....
Do as you see fit. I favor a simple design when it will work well, so in this case, a global variable seems appropreate for the job....
LFS
One Handed Man \( OHM - Terry Burns \) - 23 Oct 2004 14:22 GMT Thanks very much for your help here, it has assisted me greatly.
I am not really trying to build a proper space invaders as you correctly suggest, just trying to get a handle on threading issues which is something I have not had to do 'much' of in the past other than one or two cursory routines which would have tripped the UI.
I think I picked an awkward example unbeknown to me, however, it has served to illustrate several points to me as far as design consideration is concerned.
Thanks Again . . .
Best Regards - OHM
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> "One Handed Man ( OHM - Terry Burns )" <news.microsoft.com> wrote >> I have implemented your solution in my code and it works. However, the [quoted text clipped - 55 lines] > > LFS Ken Tucker [MVP] - 21 Oct 2004 19:28 GMT Hi,
First cant update the ui from a thread. http://msdn.microsoft.com/msdnmag/issues/04/05/BasicInstincts/
Second. You might be better of with synclock instead a mutex
Public Sub DrawImage(ByVal g As Graphics, ByVal i As Image, ByVal p As Point)
synclock g
g.DrawImage(i, p)
end synclock End Sub
Ken ----------------- Assumes a Form with a Panel on it., Does the Mutex have to be within the address of a thread start address ?
Cheers - OHM
'----------- *************** ----------------
Private endProgram As Boolean = False
Dim image1 As Image = Image.FromFile("..\Images\gun.bmp")
Dim image2 As Image = Image.FromFile("..\Images\Invader.bmp")
Dim space As Graphics
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Thread1 As New Threading.Thread(AddressOf Me.WriteImage1)
Dim Thread2 As New Threading.Thread(AddressOf Me.WriteImage2)
space = Panel1.CreateGraphics()
Thread1.Start()
Thread2.Start()
End Sub
Private Sub WriteImage1()
While Not endProgram
DrawImage(space, image1, New Point(10, 10))
End While
End Sub
Private Sub WriteImage2()
While Not endProgram
DrawImage(space, image2, New Point(50, 50))
End While
End Sub
Public Sub DrawImage(ByVal g As Graphics, ByVal i As Image, ByVal p As Point)
Dim m As New Threading.Mutex
m.WaitOne()
g.DrawImage(i, p)
m.ReleaseMutex()
End Sub
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
One Handed Man \( OHM - Terry Burns \) - 21 Oct 2004 21:59 GMT tried Synclock, that doesent seem to work
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> Hi, > [quoted text clipped - 80 lines] > > End Sub CJ Taylor - 21 Oct 2004 19:58 GMT Don't declare the mutex at the procedure level, perhaps try to at the module/class level. Otherwise, each time you enter the procedure you create a new (and unnamed) mutex variable which the other thread doesn't care about. So of course it tries to write.
Alright, so after doing some more reading, was looking at the waithandle and how that works. Look into AutoResetEvent, in which case its defined as the threads communicating through events... I can help more in a minute, but have to finish something else now and wanted to get this out to you. It inherits from WaitHandle (same as Mutex waitone). but has a few more options that do the dirty work under the sheets...
Check it out and let me know if it helps.. -CJ
I'll write more shortly.
> Assumes a Form with a Panel on it., Does the Mutex have to be within the > address of a thread start address ? [quoted text clipped - 58 lines] > > End Sub One Handed Man \( OHM - Terry Burns \) - 21 Oct 2004 22:00 GMT tried declaring at module level, that didnt work. Tried Synclock, that didnt work either
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> Don't declare the mutex at the procedure level, perhaps try to at the > module/class level. Otherwise, each time you enter the procedure you [quoted text clipped - 81 lines] >> >> End Sub CJ Taylor - 22 Oct 2004 01:23 GMT Try AutoResetEvent
> tried declaring at module level, that didnt work. Tried Synclock, that > didnt work either [quoted text clipped - 84 lines] >>> >>> End Sub One Handed Man \( OHM - Terry Burns \) - 22 Oct 2004 08:12 GMT Same problem
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> Try AutoResetEvent > [quoted text clipped - 90 lines] >>>> >>>> End Sub CJ Taylor - 22 Oct 2004 13:22 GMT Alright... lets go into more detail... how is it failing...
> Same problem > [quoted text clipped - 92 lines] > >>>> > >>>> End Sub One Handed Man \( OHM - Terry Burns \) - 22 Oct 2004 14:46 GMT If you past my code into a form and add a panel etc, you will see. If comes back with a "Object in use elsewhere", Larry posted some code which I tried in the configuration in which he tested it and it seemed to work. I was going to try it out in my code and see what transpires. I'm not particulary experienced at threading so this is newish to me.
 Signature OHM ( Terry Burns ) * Use the following to email me *
Dim ch() As Char = "ufssz/cvsotAhsfbuTpmvujpotXjui/OFU".ToCharArray() For i As Int32 = 0 To ch.Length - 1 ch(i) = Convert.ToChar(Convert.ToInt16(ch(i)) - 1) Next Process.Start("mailto:" & New String(ch)) --
> Alright... lets go into more detail... how is it failing... > [quoted text clipped - 112 lines] >> >>>> Process.Start("mailto:" & New String(ch)) >> >>>> --
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|