有没有一种方法可以知道由ThreadPool中的活动线程执行的任务?

时间:2019-04-29 16:32:56

标签: .net multithreading service 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()

1 个答案:

答案 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