StackOverflowException in Thread

G

Guest

I've written code that uses a thread to read a 70K line CSV file one line at
a time, however, after about 9 to 10 thousand lines into the file I get a
StackOverflowException while the thread tries to update a counter.

I'm sure I'm doing this correctly but if anyone can tell me different I'd
appreciate it.

ssStrip is a StatusStrip on the main form.

Private WithEvents m_tHistorical As BackgroundWorker = New BackgroundWorker()

Private Sub cmdRun_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdRun.Click
Me.ssStrip.Items(1).Visible = True
Me.ssStrip.Items(2).Visible = True
m_tHistorical.RunWorkerAsync(New
StreamReader("SSG_Historical-29-03-2007.csv", False))
End Sub

Private Sub m_tHistorical_DoWork(ByVal sender As Object, ByVal e As
System.ComponentModel.DoWorkEventArgs) Handles m_tHistorical.DoWork
e.Result = ReadHistoricalCSV(CType(sender, BackgroundWorker), e)
End Sub

Private Function ReadHistoricalCSV(ByVal W As BackgroundWorker, ByVal e
As DoWorkEventArgs) As Boolean
Dim S As StreamReader = CType(e.Argument(), StreamReader), Result As
Boolean = False
Static Rows As Integer = 0
If (W.CancellationPending()) Then
e.Cancel = True
Else
Result = S.EndOfStream()
If (Result) Then
S.Close()
Else
S.ReadLine()
Rows += 1
UpdateHCSVRows(Rows) ' <-- This is the line with the
StackOverFlow
Result = ReadHistoricalCSV(W, e)
End If
End If
Return Result
End Function

Private Sub UpdateHCSVRows(ByVal R As Integer)
If (Me.ssStrip.InvokeRequired()) Then
Me.Invoke(New CSVHRowsCallback(AddressOf UpdateHCSVRows), New
Object() {R})
Else
Me.ssStrip.Items(1).Text = FormatNumber(R, 0,
TriState.UseDefault, TriState.UseDefault, TriState.True)
End If
End Sub
 
C

Chris Dunaway

I've written code that uses a thread to read a 70K line CSV file one line at
a time, however, after about 9 to 10 thousand lines into the file I get a
StackOverflowException while the thread tries to update a counter.

Private Sub UpdateHCSVRows(ByVal R As Integer)
If (Me.ssStrip.InvokeRequired()) Then
Me.Invoke(New CSVHRowsCallback(AddressOf UpdateHCSVRows), New
Object() {R})
Else
Me.ssStrip.Items(1).Text = FormatNumber(R, 0,
TriState.UseDefault, TriState.UseDefault, TriState.True)
End If
End Sub

Your Sub above is calling itself recursively! That's why you are
getting the StackOverflowException. Try moving the InvokeRequired
check *outside* the Sub.

Chris
 
G

Guest

Chris Dunaway said:
Your Sub above is calling itself recursively! That's why you are
getting the StackOverflowException. Try moving the InvokeRequired
check *outside* the Sub.

Not to sure what you mean here as this is required when updating the UI from
a different thread.

What I did try was to comment out all of the lines apart from
Me.ssStrip.Items... one and it ran updating the row count until it got to the
10K mark and again failed with the same error.
 
S

Smokey Grindle

Me.Invoke(New CSVHRowsCallback(AddressOf UpdateHCSVRows), New
Object() {R})

your code there is callign itself, which puts a new stack frame onto the
stack, eventually it will overflow the stack because you got it stuck in a
never ending loop
 
G

Guest

Smokey Grindle said:
Me.Invoke(New CSVHRowsCallback(AddressOf UpdateHCSVRows), New
Object() {R})

your code there is callign itself, which puts a new stack frame onto the
stack, eventually it will overflow the stack because you got it stuck in a
never ending loop

Fair enough I can see this and understand, but I was only following what I
saw on the MSDN. Does anyone know how I should re-write this to fix it?
 
G

Guest

Bubba said:
Fair enough I can see this and understand, but I was only following what I
saw on the MSDN. Does anyone know how I should re-write this to fix it?

Ok with that said I've changed the code to the following:

Private Function ReadHistoricalCSV(ByVal W As BackgroundWorker, ByVal e
As DoWorkEventArgs) As Boolean
Dim S As StreamReader = CType(e.Argument(), StreamReader), Result As
Boolean = False
Static Rows As Integer = 0
If (W.CancellationPending()) Then
e.Cancel = True
Else
Result = S.EndOfStream()
If (Result) Then
S.Close()
Else
S.ReadLine()
Rows += 1
Me.ssStrip.Items(1).Text = FormatNumber(Rows, 0,
TriState.UseDefault, TriState.UseDefault, TriState.True)
Result = ReadHistoricalCSV(W, e)
End If
End If
Return Result
End Function

And I still get an error, again this procedure is calling itself, as is
shown in the MSDN so I'm assuming that this is the correct way, as it is the
only way I can see that allows you to cancel the thread's execution.

I'm at a loss with this one but I really need to get this resolved as I have
another 2 files just as large that need proccessing. I'm using threads so
that the processing of all three files can be done simultaneously to speed up
the process and make use of the quad processor based machine it runs on.

Can anyone help me out here?
 
S

Stephany Young

Well, the point is that you're still calling 'yourself' recursively thus
causing the stack to blow out.

You need to work on the 'keep it simple' principle and not turn a simple
task into a complicated one.

Try:

Private Sub ReadHistoricalCSV(ByVal W As BackgroundWorker, ByVal e As
DoWorkEventArgs)

Dim Rows As Integer = 0

Dim S As StreamReader = CType(e.Argument, StreamReader)

While Not S.EndOfStream AndAlso Not W.CancellationPending
S.ReadLine()
Rows += 1
Me.ssStrip.Items(1).Text = FormatNumber(Rows, 0, TriState.UseDefault,
TriState.UseDefault, TriState.True)
Loop

If W.CancellationPending Then
e.Cancel
Else
S.Close()
End If

End Sub

Note that the method it is now a Sub rather than a Function because it does
not need a return value.

The method loops until EndOfStream is true on the StreamReader object or
CancellationPending is set on the BackgroundWorker object, whichever occurs
first.
 
G

Guest

Stephany Young said:
Well, the point is that you're still calling 'yourself' recursively thus
causing the stack to blow out.

You need to work on the 'keep it simple' principle and not turn a simple
task into a complicated one.

Try:

Private Sub ReadHistoricalCSV(ByVal W As BackgroundWorker, ByVal e As
DoWorkEventArgs)

Dim Rows As Integer = 0

Dim S As StreamReader = CType(e.Argument, StreamReader)

While Not S.EndOfStream AndAlso Not W.CancellationPending
S.ReadLine()
Rows += 1
Me.ssStrip.Items(1).Text = FormatNumber(Rows, 0, TriState.UseDefault,
TriState.UseDefault, TriState.True)
Loop

If W.CancellationPending Then
e.Cancel
Else
S.Close()
End If

End Sub

Note that the method it is now a Sub rather than a Function because it does
not need a return value.

The method loops until EndOfStream is true on the StreamReader object or
CancellationPending is set on the BackgroundWorker object, whichever occurs
first.

Thanks Stephany,

I see the logic of this approach and with a bit of tweaking it works,
however I'm not to sure as to why the MSDN examples the use of re-entrant
threads. I guess there must be a reason for it, but one that escapes me for
now.
 
M

Michel Posseth [MCP]

When i write multithreaded proggy`s i use seperate methods for UI updating
now in these methods you can use invokerequired without anny problems
of reentering your worker method


regards

Michel [MCP]
 

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