Private Class ThreadState
Private mDirInfo As DirectoryInfo
Public Property DirInfo As DirectoryInfo
Get
Return mDirInfo
End Get
Set(ByVal value As DirectoryInfo)
mDirInfo = value
End Set
End Property
'Sum of size of all files in this directory (non-recursive.)
Private mDirectorySize As Long
Public Property DirectorySize As Long
Get
Return mDirectorySize
End Get
Set(ByVal value As Long)
mDirectorySize = value
End Set
End Property
Public Sub New(ByVal DirInfo As DirectoryInfo)
Me.DirInfo = DirInfo
End Sub
End Class
'Track files which match the criteria
Private FileList As New List(Of FileInfo)
'Keep track of number of threads still unfinished
Private mNumActiveThreads As Integer
'Allow worker threads to signal back to main thread via waithandle.
Private mEv As New ManualResetEvent(False)
'This is just a handy list to maintain all the directory size information that is gathered.
'ThreadState objects are passed into the procedure used by the individual threads.
Private ThreadStateList As New List(Of ThreadState)
''' <summary>
''' This is the main routine that adds each directory as a
''' new threadpool work item.
''' </summary>
''' <param name="StartFolder"></param>
''' <remarks></remarks>
Public Sub ScanAllFilesMultithreaded(ByVal StartFolder As Object)
FolderSize = 0
NumFiles = 0
mEv.Reset()
Dim di As New DirectoryInfo(CStr(StartFolder))
Dim DirInfoList As New List(Of DirectoryInfo)
GetDirectories(di, DirInfoList)
sw = Stopwatch.StartNew
mNumActiveThreads = DirInfoList.Count
For Each di In DirInfoList
AddWorkItem(di)
Next
'WaitOne blocks until Set is called on the object in the
'worker thread.
mEv.WaitOne()
sw.Stop()
End Sub
''' <summary>
''' This adds items to the ThreadState list
''' and adds new work items to the thread pool, all
''' based on the directoryinfo object that is passed in.
''' </summary>
''' <param name="di"></param>
''' <remarks></remarks>
Private Sub AddWorkItem(ByVal di As DirectoryInfo)
'Dim Ev As New ManualResetEvent(False)
Dim ts As New ThreadState(di)
'EventList.Add(Ev)
ThreadStateList.Add(ts)
Threading.ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf ScanFiles), ts)
End Sub
''' <summary>
''' This is the procedure called by the thread pool work items.
''' See AddWorkItem for how this is set up.
''' </summary>
''' <param name="state"></param>
''' <remarks></remarks>
Private Sub ScanFiles(ByVal state As Object)
Dim TS As ThreadState = CType(state, ThreadState)
Try
Dim FInfo As FileInfo
For Each FInfo In TS.DirInfo.GetFiles
'Since this routine can run simultaneously
'in different threads, it is important to use
'the Interlocked Increment and Add methods to ensure
'that the form level variables are thread safe and
'not subject to race conditions.
Interlocked.Increment(NumFiles)
Interlocked.Add(TS.DirectorySize, FInfo.Length)
If FInfo.Length > Limit Then
'SyncLock ensures that only this thread
'can access the filelist during this Add
SyncLock so
FileList.Add(FInfo)
End SyncLock
End If
Next
Interlocked.Add(FolderSize, TS.DirectorySize)
Catch ex As Exception
Debug.WriteLine(ex)
Finally
'When mNumActiveThreads reaches 0, all the workers are finished.
If Interlocked.Decrement(mNumActiveThreads) = 0 Then
mEv.Set()
End If
End Try
End Sub
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Commented:
I do have one question...
I'm inexperienced with .NET and the ThreadPool, but it appears to me that the code starts a thread for every folder in the StartFolder dir. I would expect that Task Manger would show dozens of new threads. But instead, it starts with 15, and climbs to only about 20. I get similar numbers if I insert a ThreadPool.GetAvailableThr
Is this as expected?
Incidently, singled-theaded my quad-core maxes at 25% (as expected). Multi-threaded, it goes up to 95% (firing on all four cylinders) -- that's just to confirm that it is working as advertized :-)
-- Dan