Thread, UI update is this ok?

P

Peter Proost

Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub


End Class
 
P

Patrick Steele

Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

I like to keep the logic of marshalling between threads local to the
method that is accessing the UI. Like this (haven't coded in VB.NET in
a while so I might be little rusty on the syntax):

Public Sub IncreaseValue(ByVal myVal As Integer)
If progy.InvokeRequired Then
progy.Invoke(New Feedback(AddressOf IncreaseValue), _
new Object() {i})
return
end if

progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
end sub

Now you just need to call IncreaseValue directly in your
"StartUpdating" method -- you don't need the multithreading stuff in
your loop code.

This also has the benefit that if you ever move this method into the UI
thread, the "progy.InvokeRequired" will return false and you'll update
the control right away -- no need to use Invoke. While it's not a huge
expense to call Invoke, why call it when you don't have to? :)
 
P

Peter Proost

Hi,

Thanks for the explanation

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

Patrick Steele said:
Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

I like to keep the logic of marshalling between threads local to the
method that is accessing the UI. Like this (haven't coded in VB.NET in
a while so I might be little rusty on the syntax):

Public Sub IncreaseValue(ByVal myVal As Integer)
If progy.InvokeRequired Then
progy.Invoke(New Feedback(AddressOf IncreaseValue), _
new Object() {i})
return
end if

progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
end sub

Now you just need to call IncreaseValue directly in your
"StartUpdating" method -- you don't need the multithreading stuff in
your loop code.

This also has the benefit that if you ever move this method into the UI
thread, the "progy.InvokeRequired" will return false and you'll update
the control right away -- no need to use Invoke. While it's not a huge
expense to call Invoke, why call it when you don't have to? :)
 
P

Peter Proost

Just to make sure, is this what you mean?

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
IncreaseValue(i)
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
If progy.InvokeRequired Then
progy.Invoke(New FeedBack(AddressOf IncreaseValue), New Object()
{myVal})
Return
End If

progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

Peter Proost said:
Hi,

Thanks for the explanation

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

if
it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

I like to keep the logic of marshalling between threads local to the
method that is accessing the UI. Like this (haven't coded in VB.NET in
a while so I might be little rusty on the syntax):

Public Sub IncreaseValue(ByVal myVal As Integer)
If progy.InvokeRequired Then
progy.Invoke(New Feedback(AddressOf IncreaseValue), _
new Object() {i})
return
end if

progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
end sub

Now you just need to call IncreaseValue directly in your
"StartUpdating" method -- you don't need the multithreading stuff in
your loop code.

This also has the benefit that if you ever move this method into the UI
thread, the "progy.InvokeRequired" will return false and you'll update
the control right away -- no need to use Invoke. While it's not a huge
expense to call Invoke, why call it when you don't have to? :)
 
P

Patrick Steele

Yup. And you can get rid of the giveFeedback variable in StartUpdating
since you're no longer using it.
 
P

Peter Proost

Thanks again, and one more question do you maybe have some good links for
reading up some more on multi threading and ui or just multi threading.

Greetz, Peter
 
P

Patrick Steele

Thanks again, and one more question do you maybe have some good links for
reading up some more on multi threading and ui or just multi threading.

No, sorry. Nothing bookmarked.
 
C

Cor Ligthert [MVP]

Peter,

I have no time to test your code, however the Queue class is in my idea
greath to use for multithreading.

You can use an thrown back event or just a timer in your mainthread to see
if that is filled and empty it while using the synclock. Mostly you see a
lot of in my eyes more dificult solutions.

http://msdn2.microsoft.com/en-us/library/system.collections.queue.aspx

http://msdn2.microsoft.com/en-us/library/3a86s51t.aspx

Jon Skeet has written a complete page about Multithreading, however in my
eyes he wants to use multithreading to often, there are very few situations
where there is no dependency or another reason that using multithreading has
sense (The only reason I see here often stated is to keep the X (end) button
alive. In my idea is that the horse behind the cart. You should make your
application faster).

(It has only sense as an offline supplier needs more time and you can spread
the proces. By instance if you are downloading and you have a speed of 64Kb
and your supplier 20Gb, than multithreading is whithouth sense. In the
oposite way it can make sense if you are downloading from suppliers which
have all 64Kb lines and you have 20Gb. However in Belgium is the avarage
speed of Internet lines so high that this makes probably as well no sense).

http://www.yoda.arachsys.com/csharp/

I hope this gives some ideas,

Cor








Peter Proost said:
Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if
it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just
keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub


End Class
 
P

Peter Proost

Hi Cor, thanks for your explanation. What I'm doing is writing in import
module from foxpro to vb.net, but I can't write entirely on the server-side
because a lot of conversions and a bad table structure in foxpro (no primary
keys), maybe it's possible to write it server-side but it's a lot easier to
write it in vb.net. A user can start this import and it runs for 5 minutes
or so for 20000 records. The problem ofcourse was the ui freezing, so I used
Dim t As New Threading.Thread(AddressOf ImportModule)
t.Start()

and this got me interested in multithreading so I started reading up on it.
I don't actualy plan on using it in this scenario as the method I now use
seems ok to me. Do you see something wrong with it? Would you advise an
other way?

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

Cor Ligthert said:
Peter,

I have no time to test your code, however the Queue class is in my idea
greath to use for multithreading.

You can use an thrown back event or just a timer in your mainthread to see
if that is filled and empty it while using the synclock. Mostly you see a
lot of in my eyes more dificult solutions.

http://msdn2.microsoft.com/en-us/library/system.collections.queue.aspx

http://msdn2.microsoft.com/en-us/library/3a86s51t.aspx

Jon Skeet has written a complete page about Multithreading, however in my
eyes he wants to use multithreading to often, there are very few situations
where there is no dependency or another reason that using multithreading has
sense (The only reason I see here often stated is to keep the X (end) button
alive. In my idea is that the horse behind the cart. You should make your
application faster).

(It has only sense as an offline supplier needs more time and you can spread
the proces. By instance if you are downloading and you have a speed of 64Kb
and your supplier 20Gb, than multithreading is whithouth sense. In the
oposite way it can make sense if you are downloading from suppliers which
have all 64Kb lines and you have 20Gb. However in Belgium is the avarage
speed of Internet lines so high that this makes probably as well no sense).

http://www.yoda.arachsys.com/csharp/

I hope this gives some ideas,

Cor








Peter Proost said:
Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if
it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just
keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub


End Class
 
C

Cor Ligthert [MVP]

Peter,

I am not sure if I have time, but if I have, will try it in the weekend.

Cor

Peter Proost said:
Hi Cor, thanks for your explanation. What I'm doing is writing in import
module from foxpro to vb.net, but I can't write entirely on the
server-side
because a lot of conversions and a bad table structure in foxpro (no
primary
keys), maybe it's possible to write it server-side but it's a lot easier
to
write it in vb.net. A user can start this import and it runs for 5 minutes
or so for 20000 records. The problem ofcourse was the ui freezing, so I
used
Dim t As New Threading.Thread(AddressOf ImportModule)
t.Start()

and this got me interested in multithreading so I started reading up on
it.
I don't actualy plan on using it in this scenario as the method I now use
seems ok to me. Do you see something wrong with it? Would you advise an
other way?

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

Cor Ligthert said:
Peter,

I have no time to test your code, however the Queue class is in my idea
greath to use for multithreading.

You can use an thrown back event or just a timer in your mainthread to
see
if that is filled and empty it while using the synclock. Mostly you see a
lot of in my eyes more dificult solutions.

http://msdn2.microsoft.com/en-us/library/system.collections.queue.aspx

http://msdn2.microsoft.com/en-us/library/3a86s51t.aspx

Jon Skeet has written a complete page about Multithreading, however in my
eyes he wants to use multithreading to often, there are very few situations
where there is no dependency or another reason that using multithreading has
sense (The only reason I see here often stated is to keep the X (end) button
alive. In my idea is that the horse behind the cart. You should make your
application faster).

(It has only sense as an offline supplier needs more time and you can spread
the proces. By instance if you are downloading and you have a speed of 64Kb
and your supplier 20Gb, than multithreading is whithouth sense. In the
oposite way it can make sense if you are downloading from suppliers which
have all 64Kb lines and you have 20Gb. However in Belgium is the avarage
speed of Internet lines so high that this makes probably as well no sense).

http://www.yoda.arachsys.com/csharp/

I hope this gives some ideas,

Cor








Peter Proost said:
Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if
it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just
keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub


End Class

--
Programming today is a race between software engineers striving to
build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)
 
T

Tom Shelton

Peter said:
Thanks again, and one more question do you maybe have some good links for
reading up some more on multi threading and ui or just multi threading.

Greetz, Peter

Peter:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnforms/html/winforms01232003.asp

This is part one of a 3 part article on the subject of interacting with
a UI from a background thread. The code is in C#, but the problem and
solutions are the same for VB.NET. Also, keep in mind that in VB 2005,
these simple scenarios are made easy by the addition of the
BackgroundWorker component.
 
C

Cor Ligthert [MVP]

Response send in Dutch by email.

Cor

Peter Proost said:
Hi Cor, thanks for your explanation. What I'm doing is writing in import
module from foxpro to vb.net, but I can't write entirely on the
server-side
because a lot of conversions and a bad table structure in foxpro (no
primary
keys), maybe it's possible to write it server-side but it's a lot easier
to
write it in vb.net. A user can start this import and it runs for 5 minutes
or so for 20000 records. The problem ofcourse was the ui freezing, so I
used
Dim t As New Threading.Thread(AddressOf ImportModule)
t.Start()

and this got me interested in multithreading so I started reading up on
it.
I don't actualy plan on using it in this scenario as the method I now use
seems ok to me. Do you see something wrong with it? Would you advise an
other way?

Greetz Peter

--
Programming today is a race between software engineers striving to build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)

Cor Ligthert said:
Peter,

I have no time to test your code, however the Queue class is in my idea
greath to use for multithreading.

You can use an thrown back event or just a timer in your mainthread to
see
if that is filled and empty it while using the synclock. Mostly you see a
lot of in my eyes more dificult solutions.

http://msdn2.microsoft.com/en-us/library/system.collections.queue.aspx

http://msdn2.microsoft.com/en-us/library/3a86s51t.aspx

Jon Skeet has written a complete page about Multithreading, however in my
eyes he wants to use multithreading to often, there are very few situations
where there is no dependency or another reason that using multithreading has
sense (The only reason I see here often stated is to keep the X (end) button
alive. In my idea is that the horse behind the cart. You should make your
application faster).

(It has only sense as an offline supplier needs more time and you can spread
the proces. By instance if you are downloading and you have a speed of 64Kb
and your supplier 20Gb, than multithreading is whithouth sense. In the
oposite way it can make sense if you are downloading from suppliers which
have all 64Kb lines and you have 20Gb. However in Belgium is the avarage
speed of Internet lines so high that this makes probably as well no sense).

http://www.yoda.arachsys.com/csharp/

I hope this gives some ideas,

Cor








Peter Proost said:
Hi group,

I have been doing some reading on threading and updating the ui from a
worker thread and I made this sample which works but I was wondering if
it's
the ok way to do it? Can I improve something? What's the best practise for
updating multiple controls on my form for example 5 labels or so, just
keep
passing them to the constructor?

Greetz, Peter

On my Form:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oMyUpd As New MyUpdater(ProgressBar1, lblPct)
oMyUpd.Start()

End Sub

'The worker thread

Imports System.Threading

'======================================================================
Public Class MyUpdater
Delegate Sub UpdateStarter()
Delegate Sub FeedBack(ByVal myValue As Integer)

Private oThread As Thread
Private oMyUpdate As New UpdateStarter(AddressOf StartUpdating)

Private progy As ProgressBar
Private infoLbl As Label

Public Sub New(ByVal myProg As ProgressBar, ByVal myLabel As Label)
progy = myProg
infoLbl = myLabel
End Sub

Public Sub [Start]()
oMyUpdate.BeginInvoke(Nothing, Nothing)
End Sub

Public Sub StartUpdating()
Dim giveFeedBack As New FeedBack(AddressOf IncreaseValue)
oThread = Thread.CurrentThread
For i As Integer = 0 To 15
progy.BeginInvoke(giveFeedBack, New Object() {i})
Thread.Sleep(200)
Next

End Sub

Public Sub IncreaseValue(ByVal myVal As Integer)
progy.Value = myVal
infoLbl.Text = CStr(Math.Round(myVal / progy.Maximum * 100, 2))
End Sub


End Class

--
Programming today is a race between software engineers striving to
build
bigger and better idiot-proof programs, and the Universe trying to produce
bigger and better idiots. So far, the Universe is winning. (Rich Cook)
 

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