背景...
我有一个Windows服务,其中包含许多按计划运行的组件。每个组件都会处理文件,但会根据文件在系统中的当前状态对文件执行特定的任务。这些组件当前在服务中的每个组件内为每个正在处理的文件创建线程(System.Threading.Thread)。
组件1可能正在从FTP站点提取文件。
组件2可能正在将文件解压缩到硬盘驱动器。
组件3可能正在解密硬盘驱动器上的文件。
组件4可能正在将文件从一个位置复制或移动到另一个位置。
当前,每个组件在每个正在处理的文件中,在新线程中启动其特定于组件的任务。这对我们来说效果很好,但是随着系统和公司的发展,它变得越来越难以管理。我正在研究ThreadPool(System.Threading.ThreadPool),以便更轻松地进行线程管理和总体上更好地进行资源管理。
这是当前的简化设计...
'send a message that the component task has begun
Comms.ComponentStatus(ComponentID, Running)
For Each f As File
Dim t As New Thread(AddressOf processFile)
t.Start(f)
lst.Add(t)
Next
'some list checking to see if all threads in list are done
Do Until lst has no active threads
Loop
'all threads complete so the component task is complete
Comms.ComponentStatus(ComponentID, Complete)
我的困境...
我创建了一个仪表板,用于接收有关每个组件和正在执行的任务的实时消息(.Net Remoting)。跟踪信息,异常信息,最重要的是整个组件任务的开始和结束。在我当前的设计中,我告知任务已开始,为每个要处理的文件创建线程,并跟踪创建的线程。我查看了为任务创建的所有线程,当它们全部完成时,我会通知任务已完成。这对我们来说很好。使用ThreadPool设计时,我所有的组件都将从一个进程范围的线程池中拉出线程,从而允许系统管理它们,但不允许我知道每个组件中哪些线程用于哪些任务,因此不允许我知道组件的任务何时完成。
快速浏览.Net的ThreadPool并不能告诉我可以确定池中哪些活动线程正在执行哪些任务。有没有人有解决方案或建议?预先感谢。
'this only returns a bool telling me the requested task will be performed
ThreadPool.QueueUserWorkItem
'this only returns to me a number of threads available for queue
ThreadPool.GetAvailableThreads()
答案 0 :(得分:0)
我决定要做的是创建一个类,该类保留有关正在执行的任务的信息,包括组件的ID,任务的ID和线程计数。每当任务运行时,它将在组件级别(类级别)创建此对象的实例。这使我能够在组件级别隔离线程计数。当它在ThreadPool中的线程排队时,它会增加对象中的线程计数器。当所有线程都已排队后,它就坐下来等待线程计数器返回0,因此将任务标记为完成。每当线程的委托完成处理(线程完成)时,线程计数器都会减少。我设置了一个全面的时间跨度,以防万一发生意外情况。快速的代码和测试从概念上向我展示了它的确起作用。我将继续进行编码和测试,如果发现任何可导致更改的发现,请在此处发布。
这是我对线程跟踪对象的初步概述。
Public Class TaskTracker : Implements IDisposable
Public ReadOnly Property ComponentID As Integer = 0
Public ReadOnly Property TaskUID As Guid = Guid.Empty
Public ReadOnly Property ThreadCount As Integer = 0
''' <summary>
''' Create a new instance of the TaskTracker object.
''' </summary>
''' <param name="ComponentID">The ID of the Component this object belongs to.</param>
''' <param name="TaskUID">The UID of the Task in question.</param>
Public Sub New(ComponentID As Integer, TaskUID As Guid)
Try
_ComponentID = ComponentID
_TaskUID = TaskUID
_ThreadCount = 0
Catch ex As Exception
Log.Save(Log.Types.Error, ComponentID, TaskUID, ex.Message, ex.StackTrace)
End Try
End Sub
''' <summary>
''' Increment the internal thread count property by the amount in the value provided.
''' </summary>
''' <param name="Value">The amount to increment the thread count by.</param>
Public Sub IncrementThreadCount(Optional Value As Integer = 1)
Try
_ThreadCount += Value
Catch ex As Exception
Log.Save(Log.Types.Error, ComponentID, TaskUID, ex.Message, ex.StackTrace)
End Try
End Sub
''' <summary>
''' Decrement the internal thread count property by the amount in the value provided.
''' </summary>
''' <param name="Value">The amount to decrement the thread count by.</param>
Public Sub DecrementThreadCount(Optional Value As Integer = 1)
Try
If _ThreadCount > 0 Then
_ThreadCount -= Value
Else
_ThreadCount = 0
End If
Catch ex As Exception
Log.Save(Log.Types.Error, ComponentID, TaskUID, ex.Message, ex.StackTrace)
End Try
End Sub
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
End If
_ComponentID = 0
_TaskUID = Guid.Empty
_ThreadCount = 0
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
End Class
这是它的(缩写)实现...
Private Shared taskTrack As TaskTracker = Nothing
Public Shared Function Start() As ResultPackage
Try
TaskUID = Guid.NewGuid()
taskTrack = New TaskTracker(ComponentID, TaskUID)
'let any listeners know the task has started
Comms.ComponentStatus(ComponentID, True)
'mark the start time of the total task
compStart = Now
Log.Save(Log.Types.Trace, ComponentID, TaskUID, _ClassName & " Started", "Successful start of the " & _ClassName & " component.")
For Each cli As ClientMaster In ClientMaster.GetList(True)
'inner try/catch so that we can continue to process clients even if one errors
Try
ThreadPool.QueueUserWorkItem(AddressOf processClient, cli)
Log.Save(Log.Types.Trace, ComponentID, TaskUID, "Client Thread Queued", "Thread queued for Client [" & cli.ClientName & "].")
taskTrack.IncrementThreadCount()
Catch ex As Exception
Log.Save(Log.Types.Error, ComponentID, TaskUID, ex.Message, ex.StackTrace)
End Try
Next
Do Until taskTrack.ThreadCount = 0 'or some timespan has been reached for a catchall
Thread.Sleep(500)
Loop
Comms.ComponentStatus(ComponentID, False)
'mark the end time of the total task
compEnd = Now
Log.Save(Log.Types.Trace, ComponentID, TaskUID, _ClassName, "Successful end of the " & _ClassName & " component.")
Log.Save(Log.Types.Trace, ComponentID, TaskUID, _ClassName & " Task Time", _ClassName & " task took " & FriendlyTimeSpan(DateDiff(DateInterval.Second, compStart, compEnd)) & " to complete.")
Comms.ComponentMessage(ComponentID, "Task Duration: " & FriendlyTimeSpan(DateDiff(DateInterval.Second, compStart, compEnd)))
Catch ex As Exception
resPack.Result = ResultPackage.Results.Fail
resPack.Alerts.Add(New ResultPackage.Alert("Exception in Start()", ex))
Log.Save(Log.Types.Error, ComponentID, TaskUID, ex.Message, ex.StackTrace)
Throw
End Try
Return resPack
End Function
Private Shared Sub processClient(Client As ClientMaster)
'do work
'decrease the thread counter since this task is complete
taskTrack.DecrementThreadCount()
End Sub