Multithreading dilemma

D

dan artuso

Hi,
I'm having a problem implementing multithreading in
an app that basically recurses through the path passed to it.

The main interface is basically an explorer knockoff and the user can
perform certain
tasks on all files and directories within their selection.

The problem is that for a little bit the main UI is responsive after spawing
the thead but continued
use of it causes both the main thread and the worker thread to freeze up. If
we don't touch the UI at all,
everything works :)
Note that we are not trying to update any form controls from the worker
thread!

I'll post what code I think is relevant and anyone who has any ideas, please
let us know!
Thanks

Dan

here is code in the form:

Private Sub cmdStart_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdStart.Click
'for this quick search, the only pattern passed willl be the PermPattern

Dim objPerms As New PermPattern

'we still need a list of patterns because the Search constructor expects
it.

Dim thisPatternList As New List(Of Pattern)

'this is for each path in the listview

Dim strPaths As New List(Of String)

Dim csitems As New List(Of CShItem)

objPerms.state = PermPattern.permissions.accessDenied +
PermPattern.permissions.noAdmins + _

PermPattern.permissions.noSystem + PermPattern.permissions.notAGroup + _

PermPattern.permissions.pathTooLong

thisPatternList.Add(objPerms)

For Each lvItem As ListViewItem In Me.lvDirs.Items

Dim csItem As New CShItem(lvItem.Text)

csitems.Add(csItem)

Next

'our search object

Dim objSearch As New Search(thisPatternList, csitems, Me)

objSearch.Start()


End Sub


Here is the code in the Search class:


'rference to calling form

Private m_clientApp As Form

'create a delegate that will call back into the main form

Private Delegate Sub ProcessResults(ByVal objSearch As Search)

'create an object for the delegate

Private m_callMainForm As ProcessResults


Constructor:


Sub New(ByVal Patterns As List(Of Pattern), ByVal cshItems As List(Of
CShItem), ByVal callingForm As Form)

m_patterns = Patterns

m_results = New List(Of Result)

m_cshItems = cshItems

m_clientApp = callingForm

'dim all possible form variables

Dim policyAudit As fPolicyAudit

Dim changeOwner As fChangeOwner

'try to cast

policyAudit = TryCast(callingForm, fPolicyAudit)

changeOwner = TryCast(callingForm, fChangeOwner)

If Not IsNothing(policyAudit) Then

m_callMainForm = AddressOf policyAudit.Processresults

End If

End Sub

Protected InnerThread As New Thread(AddressOf Me.SearchFiles)

Public Sub Start()

InnerThread.SetApartmentState(ApartmentState.STA)

InnerThread.IsBackground = True

InnerThread.Priority = ThreadPriority.BelowNormal

InnerThread.Start()

End Sub

'here is the routine that starts on a seperate thread. Basically, for each
path passed in, it calls WorkWithItems which recurses through files and
folders

Public Sub SearchFiles()

Debug.Print("SearchFiles: " &
System.Threading.Thread.CurrentThread.ManagedThreadId.ToString)

m_results.Clear()

For Each csItem As CShItem In m_cshItems

WorkWithItems(csItem)

Next



Debug.Print("about to callback")

'call back to calling form passing the Search object so results can be
processed

m_clientApp.Invoke(m_callMainForm, Me)

End Sub
 
A

AMercer

You should be able to start a compute bound thread (eg For loops that churn
for about 10 seconds) and still have a responsive gui while the thread is
running. When I do this, I don't call SetApartmentState, I do set
IsBackground true as you do, and I leave Priority unchanged (BelowNormal
should be unnecessary). My first suggestion is to verify that you can do
this simple threading exercise. I have no experience with setting the
apartment state, ergo I am suspicious of it.

Second, your description makes it plausible that a resource is being
consumed. Run your program and watch your process's task manager line. Make
sure everything that can be viewed is visible. Watch for a steadily rising
number like the handle count. Alternatively, use a perf tool that you are
comfortable with.

Third, as a stab in the dark and a curiosity, I would insert a Sleep(50
milliseconds) for each file you process. Is the gui more responsive with the
sleeps? Does the freeze up still happen? IMO this is unlikely to help, but
it is easy to try.
 
M

Michael D. Ober

Dan,

Don't use SetApartmentState unless you are using COM Interop. Apartments
are part of the COM multithreading interface. I suspect removing this one
line will fix your performance problems.

Mike Ober.
 
D

Dan

Hi
Thanks for the replies.
This is a difficult thing to troubleshoot I know.

I tried putting the Sleep(50) in and no difference.

COM is involved in the Search routine.
WorkWithItems(ByVal csItem As CShItem)



CShItem is a class we use in the project that uses COM.

We didn't write this and there is tons of code in it.

I have a feeling this may be our problem. The array of CShItems gets passed
into the Search object before the new thread is started.

Altough I don't know why everything would freeze up simply by using the
MainForm which is executing on a different thread.

I also kept an eye on the resources in Task Manager and nothing was out of
the ordinary.

I appreciate the effort but I suspect we'll have to fix this thing on our
own!

Thanks again!

Dan
 
A

AMercer

From your info below, I suggest you split your single process solution into
two processes. Have your gui process launch a new process to do the work
that is apparently causing the problem and that contains the software that is
difficult or impossible to fix. When it has done its work, the process
terminates, and its ill effects will be isolated in a process that is
terminating. Starting the process should be easy enough - all you need to
give the new process is a path. If there are any results that it produces,
you will need to come up with a way to get the data back into the gui process.
 
D

dan artuso

You know, I was just thinking about the 'good old days' in VB6 where I would
simulate a thread with an ActiveX exe, spawning another process. Always
worked like a charm!

Thanks for your suggestions.

Dan
 
C

Cor Ligthert[MVP]

Dan,

What is your goal, using multithreading or making a good solution.

In my idea we see often here people who only want multithreading because it
sounds so interesting.

Mutlithreading is only in few circumstances (and they are there for sure)
giving benefits.

As soon as your processor or a port is fulltime busy, you have no benefits,
only longer processing time and difficulties not to restart a thread because
the user just pushes a button.

Cor
 
M

Michael D. Ober

Dan said:
Hi
Thanks for the replies.
This is a difficult thing to troubleshoot I know.

I tried putting the Sleep(50) in and no difference.

COM is involved in the Search routine.
WorkWithItems(ByVal csItem As CShItem)

Dan,

Given that COM is indeed involved, you're best bet will be to split the
application into two apps. COM objects aren't Win32 (or .NET) thread aware
and really need to be in seperate processes. This is even true with the
Apartment model. All the Apartment model does is prevents the COM object
from sharing it's internal data structures across multiple processes.
Instead the Apartment model COM objects will request the calling process
allocate memory - once.

Mike.
 
D

dan artuso

Well, it seems COM was the problem.

At least it started working okay when I got rid of this code in one of our
classes:

Private Function AccountType(ByVal strSid As String) As String 'As
SID_NAME_USE

'this function returns the type of account:

'Computer, User, Group, Domain, Alias, well known

Dim ret As Boolean

Dim ptrSid As IntPtr

Dim cbSid As Integer

Dim refDomainName As New System.Text.StringBuilder

Dim rDomainName As String

Dim cbRefDomainName As Integer

Dim peUse As SID_NAME_USE

'First get the buffer sizes

ret = Win32.LookupAccountName(Nothing, strSid, Nothing, cbSid,
Nothing, cbRefDomainName, peUse)

'Adjust the buffers to the needed size

ptrSid = Marshal.AllocHGlobal(cbSid)

rDomainName = refDomainName.EnsureCapacity(cbRefDomainName)

'Get the data again, now with the changed buffers

ret = Win32.LookupAccountName(Nothing, strSid, ptrSid, cbSid,
rDomainName, cbRefDomainName, peUse)

If ret Then

'If return doesn't fail return the type)

Return peUse

End If

Marshal.FreeHGlobal(ptrSid)

End Function


Dan
 
B

Brian Gideon

Well, it seems COM was the problem.

At least it started working okay when I got rid of this code in one of our
classes:

Private Function AccountType(ByVal strSid As String) As String 'As
SID_NAME_USE

'this function returns the type of account:

'Computer, User, Group, Domain, Alias, well known

Dim ret As Boolean

Dim ptrSid As IntPtr

Dim cbSid As Integer

Dim refDomainName As New System.Text.StringBuilder

Dim rDomainName As String

Dim cbRefDomainName As Integer

Dim peUse As SID_NAME_USE

'First get the buffer sizes

ret = Win32.LookupAccountName(Nothing, strSid, Nothing, cbSid,
Nothing, cbRefDomainName, peUse)

'Adjust the buffers to the needed size

ptrSid = Marshal.AllocHGlobal(cbSid)

rDomainName = refDomainName.EnsureCapacity(cbRefDomainName)

'Get the data again, now with the changed buffers

ret = Win32.LookupAccountName(Nothing, strSid, ptrSid, cbSid,
rDomainName, cbRefDomainName, peUse)

If ret Then

'If return doesn't fail return the type)

Return peUse

End If

Marshal.FreeHGlobal(ptrSid)

End Function

Dan

I see the LookupAccountName Win32 API, but I'm not seeing any COM
objects.

You shouldn't need to allocate memory from the unmanaged heap to use
that API. Let the interop marshaller do it for you. Check out
http://www.pinvoke.net for an alternate method of declaring and using
LookupAccountName.

Also, you have a *major* memory leak here. It looks like FreeHGlobal
is only called when the function fails. You should use a Try-Finally
block to work around that issue. I'm wondering if this is part of
your problem.
 

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