multithreading issue

S

Sam

Hi,
I've written the following, inspired by a MSDN article.
First I want to know if what I do is correct. The reason I'm asking is
because if the value passed to CalcPi is great(eg.10000) then I can't
interact with the UI anymore (resize window, move window...)
Second, I don't know how to close Form2. If I do frm.Close in CalcPi
after the loop, I get the exception:

Cross-thread operation not valid: Control 'Form2' accessed from a
thread other than the thread it was created on.

Quick overview of the app:
I create a delegate to open a new form when I click on a button
The new form creates a delegate of CalcPi and update a progressbar
CalcPi does a loop from 0 to X and display "test" each time in a
textbox.

Can you help?
Thanks

CODE:

Public Class Form1

Dim frm As Form2
Dim sf As ShowFormDelegate

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

sf = New ShowFormDelegate(AddressOf ShowForm)
sf.BeginInvoke(Nothing, Nothing)
End Sub

Public Delegate Sub CalcPiDelegate(ByVal digits As Integer)

Public Sub CalcPi(ByVal digits As Integer)
Dim pi As StringBuilder = New StringBuilder)

ShowProgress(pi.ToString, digits, 0)

If (digits > 0) Then
pi.Append(".")

For i As Integer = 0 To digits
pi.Append("test")
ShowProgress(pi.ToString, digits, i)
Next
'RAISED EXCEPTION HERE
frm.Close()
End If
End Sub

Delegate Sub ShowProgressDelegate(ByVal pi As String, ByVal
totalDigits As Integer, ByVal digitSoFar As Integer)

Public Sub ShowProgress(ByVal pi As String, ByVal totalDigits As
Integer, ByVal digitSoFar As Integer)
If Not piText.InvokeRequired Then
piText.Text = pi
frm.ProgressBar2.Maximum = totalDigits
frm.ProgressBar2.Value = digitSoFar
Else
Dim sp As New ShowProgressDelegate(AddressOf ShowProgress)
Dim ar As New ArrayList
ar.Add(pi.ToString)
ar.Add(totalDigits)
ar.Add(digitSoFar)
BeginInvoke(sp, ar.ToArray)
End If
End Sub

Delegate Sub ShowFormDelegate()

Public Sub ShowForm()
If Not InvokeRequired Then
frm = New Form2(Me)
frm.ShowDialog()
Else
Dim sf As New ShowFormDelegate(AddressOf ShowForm)
BeginInvoke(sf)
End If
End Sub

End Class

---------------------------------------------------------------

Public Class Form2

Private Sub Form2_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load

Dim cp As New Form1.CalcPiDelegate(AddressOf Form1.CalcPi)
cp.BeginInvoke(1000, Nothing, Nothing)

End Sub

End Class
 
H

Herfried K. Wagner [MVP]

Sam said:
I've written the following, inspired by a MSDN article.
First I want to know if what I do is correct. The reason I'm asking is
because if the value passed to CalcPi is great(eg.10000) then I can't
interact with the UI anymore (resize window, move window...)
Second, I don't know how to close Form2. If I do frm.Close in CalcPi
after the loop, I get the exception:

Cross-thread operation not valid: Control 'Form2' accessed from a
thread other than the thread it was created on.

I didn't take a look at the source code you posted, but you may find the
articles referenced in the Web page below useful:

Multithreading in Windows Forms applications
<URL:http://dotnet.mvps.org/dotnet/faqs/?id=multithreading&lang=en>
 
S

Sam

Herfried,
thanks for the link
I've already look at most of those article (my app is actually inspired
by one of them).
But I still don't understand my problem
 
S

Sam

See code above.

i give it again:

Dim cp As New Form1.CalcPiDelegate(AddressOf Form1.CalcPi)
cp.BeginInvoke(1000, Nothing, Nothing)
 
S

Sam

Ok, I've changed the way I do it using proper multithreading of the
System.Threading Class.

Now I'm facing another issue. To pass parameter to my function executed
by the thread, I've encapsulated it into a class and the parameter
correspond to member variable. To return a value I use a delegate and I
raise an event to update the progressbar:


Public Sub Calcul()
Dim i As Integer
For i = 0 To Me._digits
While Me.manualEvent.WaitOne(100, False)
End While
RaiseEvent InProgress(Me, New InProgressEventArgs(i))
Next
Me.manualEvent.Close()
RaiseEvent Complete()
End Sub

The issue is that I don't want to encapsulate all of the methods in my
project that are candidate to be executed by a thread ! That would be
silly considering the number of functions!
So how can I do a generic class? I can't pass the address of the
function to be executed to Calcul because of the code in the For loop.
 
G

Guest

Sam,

I have recently started with the problem of multiple threads and forms, and
I see that you are way, way ahead. If I may be permitted some questions, have
you seen the "Task Class" in:

David Hill's WebLog:
http://blogs.msdn.com/dphill/articles/183503.aspx

which was apparently derived from Microsoft patterns & practices:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scag-ch06.asp

If so, have you found it useful, or have you taken another approach?

In your first post in this thread, you closed a form from (I think) another
thread:

'RAISED EXCEPTION HERE
frm.Close()

How do you do this now?

Thank you kindly.

-Den
 
S

Sam

Hi Den
Sorry for late reply. No I never looked at this link before.
My multithreading is working very well now so I don't think I'm going
to change anything to it :)

Regarding the exception, basically it was raised because I was trying
to access a control (form) which had not been created by the thread I
was trying to access it from. This issue should not occur in VS2003,
but in VS2005 it does as it is more sensitive. To overcome this
problem, I use BeginInvoke and I pass it a delegate of the method I
want to access.

Hope that helps.

Sam
 

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