有没有办法知道线程池中的活动线程正在执行什么任务?

Is there a way to know what task if being performed by the active threads in the ThreadPool?

背景...
我有一个 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()

我决定做的是创建一个 Class 来保存有关正在执行的任务的信息,包括组件 ID、任务 ID 和线程计数。每当任务运行时,它都会在组件级别(Class 级别)创建此对象的实例。这使我能够在组件级别隔离线程数。当它在 ThreadPool 中对线程进行排队时,它会增加对象中的线程计数器。当所有线程都排队后,它会等待线程计数器从 return 变为 0,从而将任务标记为完成。每次线程的委托完成处理(线程完成)时,线程计数器都会递减。我设置了一个包罗万象的时间跨度以脱离线程计数循环,以防发生不可预见的事情。快速代码和测试从概念上向我展示了它确实有效。我将继续编码和测试,如果发现任何导致更改的结果,我将 post 放在这里。

这是我对线程跟踪对象的初步概述。

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