如何将List(of Task)函数实现为队列? (VB.NET)

时间:2014-06-20 15:00:14

标签: vb.net multithreading

尽可能简单:

  • 多线程WPF窗口应用
  • 所有线程都通过ssh查询单个设备。
  • 设备可以有多个同一用户,但IO堆栈不会让app(显然)。

试图采用“合法”的方式:

如何创建每个从函数返回字符串的任务/操作的队列(FIFO)?

2014-06-21更新

这就是我现在所处的位置。我可以在ConcurrentQueue上创建任务。

问题仍然是如何修改它以允许创建/调用任务,任务放入FIFO队列,以及任务的结果(String)返回到调用它的变量,如:

昏暗的结果=任务......(“命令”)......

以下是我测试方法的基础(编译和运行):

Dim TCQ As New ConcurrentQueue(Of Task)
Sub Main()
        ' This simulates program calls to Enqueue tasks on TCQ:
        For i = 1 To 20
            TCQ.Enqueue(GetTask(Date.Now.ToLongTimeString & " - Command number " & i))
        Next
        ' normally, TCQ would be watched in a loop
        Do While TCQ.Count > 0
            Dim tsk As Task = Nothing
            TCQ.TryDequeue(tsk)
            tsk.Wait()
            Console.WriteLine(Date.Now.ToLongTimeString & " [" & tsk.Id & "] post-Wait(): " & tsk.Id & " TCQ.Count: " & TCQ.Count)
        Loop
        Console.WriteLine("done.")
        Console.ReadLine()
End Sub

下一个函数创建一个包含命令字符串的任务...

Function GetTask(CommandString As String) As Task(Of String)
        Dim t As Task(Of String) = Nothing
        Dim cts As New CancellationTokenSource()
        Dim ct As CancellationToken = cts.Token
        Try
            t = Task(Of String).Factory.StartNew(Function()
                                                     Return GetCommandResults(CommandString, ct)
                                                 End Function, ct, TaskCreationOptions.LongRunning)
            t.Wait()
            Console.WriteLine("GetTask.Id: " & t.Id & " result: " & t.Result)
        Catch ex As Exception
            Console.WriteLine("Caught Exception: " & ex.ToString)
        End Try
        Return t
End Function

此功能用于将命令发送到设备,并将响应存储为字符串......

Function GetCommandResults(theCommand As String, ct As CancellationToken) As String
        Console.WriteLine(String.Format("--> starting  GetCommandResults({0})", theCommand))
        Thread.Sleep(2000)
        Console.WriteLine(String.Format("<-- done with GetCommandResults({0})", theCommand))
        Return String.Format(Date.Now.ToLongTimeString & " - GetCommandResults returned {0}", theCommand)
End Function

收益率:

[Main()] before creating tasks
--> starting  GetCommandResults(8:14:38 PM - Command number 1)
<-- done with GetCommandResults(8:14:38 PM - Command number 1)
GetTask.Id: 1 result: 8:14:40 PM - GetCommandResults returned 8:14:38 PM - Command number 1
--> starting  GetCommandResults(8:14:40 PM - Command number 2)
<-- done with GetCommandResults(8:14:40 PM - Command number 2)
GetTask.Id: 2 result: 8:14:42 PM - GetCommandResults returned 8:14:40 PM - Command number 2
--> starting  GetCommandResults(8:14:42 PM - Command number 3)
<-- done with GetCommandResults(8:14:42 PM - Command number 3)
GetTask.Id: 3 result: 8:14:44 PM - GetCommandResults returned 8:14:42 PM - Command number 3
[...]

所以,如果我必须逻辑地写出来:

  1. 使用命令字符串准备String变量作为已创建任务的输出。
  2. 将String命令放入ConcurrentQueue。
  3. 当Task从ConcurrentQueue中退出时,它将被运行,并且命令的结果将返回给调用变量。
  4. 所以,你可以说我正在寻找一种方法来做到这一点,但如上所述,同步队列:

    Dim result as String = Await Task.Factory.StartNew(Function()                                                        Dim r as String = FunctionThatReturnsString()                                                        返回r                                                    结束功能)

    ...除了我似乎无法阻止任务一旦创建就执行......

    希望此更新有所帮助!

1 个答案:

答案 0 :(得分:0)

没有比提出解决方案更好的方法了。既然没有采取任何行动(对于一个棘手的问题没有任何意义,甚至),我希望主持人会看到另一种方式,因为我问如何做得更好 - 但没有问号!

总结此解决方案的尝试:

  1. 调用者创建或使用单例JobQSingleton()。
  2. 调用者通过创建一个唯一的guid(用于以后查找结果)和作为字符串的作业来使用q:

    Dim jobId As Guid = Guid.NewGuid Dim myCommand As String =&#34; Command for Job#&#34; &安培;我

  3. 调用者创建一个Job()实例,使用上面的参数加载它,并将其提交给q:

    Dim j as Job = New Job() j.CallerJobID = jobId j.Command = myCommand j.CallerName =&#34;来电者#&#34; &安培;一世 TheJobQ.JobRequest(j)的

  4. 最后,调用者通过在TheJobQ上调用GetJobResultAsync()等待结果,使用作为&#39;索引提交的作业提供的guid,如果你愿意的话,找到结果:

    Dim jobResult As String = TheJobQ.GetJobResultAsync(CallerJobID).Result

  5. 工作示例就在这里,但它在我的脑海中留下了许多问题,一个重要的问题是,将任务放在队列上是否会更好,la:

    Dim t As New Task(AddressOf Me.TakeFromQLoop, ct, TaskCreationOptions.LongRunning)
    ...add to queue; when time, pull off queue and...
    t.Start()
    

    讨论:我无法找到一种方法来保持呼叫者在进入/离开q时附加的任务。这样做可能会少得多。这在OP中暗示过。


    幸运的是,我在一个模块中编写了这个测试/用例,以便于复制/粘贴。它在VS2012中工作:

    Imports System.Collections.Concurrent
    
    Module Module1
        Dim TheJobQ As JobQSingleton
        '
        Sub Main()
            TheJobQ = JobQSingleton.GetInstance ' only 1 class can do work (serially/synchronously)
            '
            ' step 1: create the jobs
            '
            Dim jobList As New ConcurrentQueue(Of Job)      ' each client comes up with their own Guid to later retriece their job's results
            Dim jobIdList As New ConcurrentQueue(Of Guid)   ' this list holds those Guid, so we can iterate at the end and get the results
            For i = 1 To 10
                Dim jobId As Guid = Guid.NewGuid
                Dim myCommand As String = "Command for Job #" & i
                Console.WriteLine("[{0}] CallerJobID: {1} Command: {2}", i, jobId.ToString, myCommand)
                jobList.Enqueue(New Job() With {.CallerJobID = jobId, .Command = myCommand, .CallerName = "caller #" & i})
                jobIdList.Enqueue(jobId)
            Next
            '
            ' step 2: submit requests
            '
            Threading.Thread.Sleep(1000)
            For Each jReq As Job In jobList
                If jobList.TryDequeue(jReq) Then
                    TheJobQ.JobRequest(jReq)
                End If
                Console.WriteLine("[{2}] {0} added {1} to the Q via .JobRequest", jReq.CallerName, jReq.CallerJobID, jobList.Count)
            Next
            '
            ' step 3: get results (using retrieveQ created earlier from copying jobList)
            '
            For Each clientJobId As Guid In jobIdList '.Reverse ' simulates all our clients' provided Guids
                Dim jobResult As String = GetWorkResult(clientJobId)
                Console.WriteLine("Result of client jobId: {0} is: ""{1}""", clientJobId, jobResult, jobIdList.Count)
            Next
            Console.WriteLine("done.")
            Console.ReadLine()
        End Sub
        '
        ' step 3a: 
        '
        Public Function GetWorkResult(CallerJobID As Guid) As String
            Dim r As String = ""
            r = TheJobQ.GetJobResultAsync(CallerJobID).Result
            Return r
        End Function
        '
        '========================================================================
        '
        ' JobQ()
        '
        Public Class JobQSingleton
    #Region "class vars"
            ' singleton instance
            Shared myInstance As JobQSingleton
            ' requests
            Private Q As New ConcurrentQueue(Of Job)
            ' results
            Private ResultsDictionary As New ConcurrentDictionary(Of Guid, Job)
            Private DoQLoop As Boolean
    #End Region
    #Region "constructor"
            Private Sub New()
                Me.DoQLoop = True
                Me.StartLoop()
            End Sub
            Public Shared Function GetInstance() As JobQSingleton
                If IsNothing(myInstance) Then
                    myInstance = New JobQSingleton
                End If
                Return myInstance
            End Function
            Private Sub StartLoop()
                Dim cts As New Threading.CancellationTokenSource
                Dim ct As Threading.CancellationToken = cts.Token
                ' (a) with .Start():
                'Dim t As New Task(AddressOf Me.TakeFromQLoop, ct, TaskCreationOptions.LongRunning)
                't.Start()
                ' (b) simple AddressOf:
                Dim t As Task = task.Factory.StartNew(AddressOf Me.TakeFromQLoop, ct, TaskCreationOptions.LongRunning)
                '
                Console.WriteLine("Loop started")
            End Sub
    #End Region
    #Region "(public) submit/retrieve jobs"
            '=======================================================
            '
            ' step 2a: add a guid for internal use/deugging
            '
            Public Sub JobRequest(aJobRequest As Job)
                With aJobRequest
                    .QJobID = Guid.NewGuid  ' our internal ID
                End With
                Me.AddJob(aJobRequest)
            End Sub
            Public Async Function GetJobResultAsync(ByVal jobId As Guid) As Task(Of String)
                Return Await Task.Run(Of String)(Function() GetJobResult(jobId))
            End Function
            '
            ' callers call one of the above 2 to either give a job, or get results
            '
            '=======================================================
            Private Function GetJobResult(jobId As Guid) As String
                Do Until Me.ResultsDictionary.ContainsKey(jobId)
                    Threading.Thread.Sleep(250)
                Loop
                Return Me.ResultsDictionary.Item(jobId).Response
            End Function
    #End Region
            Private Sub AddJob(j As Job)
                Q.Enqueue(j)
            End Sub
            Private Sub TakeFromQLoop()
                While (Me.DoQLoop)
                    Dim j As New Job
                    If Q.TryPeek(j) = True Then
                        If Q.TryDequeue(j) = True Then
                            With j
                                Console.WriteLine("===> Start job ID {0} from caller {1}", .CallerJobID, .CallerName)
                                Threading.Thread.Sleep(1000)
                                j.Response = String.Format("The result of the command {0} is {1}", j.Command, Date.Now.ToLongTimeString)
                                If Me.ResultsDictionary.TryAdd(j.CallerJobID, j) Then
                                    Console.WriteLine("<===  Stop job ID {0} from caller {1}", .CallerJobID, .CallerName)
                                Else
                                    Console.WriteLine("...error...")
                                End If
                            End With
                        End If
                    End If
                    Threading.Thread.Sleep(1000)
                End While
            End Sub
        End Class
        '
        ' holds the job & results...
        '
        Public Class Job
            Public Property JobName As String
            Public Property QJobID As Guid
            Public Property CallerName As String
            Public Property CallerJobID As Guid
            Public Property Command As String
            Public Property Response As String
        End Class
    End Module
    

    输出(使用上面注释掉的.Reverse选项)

    Loop started
    [1] CallerJobID: d4a1f479-1d27-4b2f-8d9c-5db1fbacf906 Command: Command for Job #1
    [2] CallerJobID: 06a4a886-4054-426b-b963-88e54d4aeda7 Command: Command for Job #2
    [3] CallerJobID: 1415bb08-607c-4bf0-9b28-a1f33eb24c33 Command: Command for Job #3
    [4] CallerJobID: 18568325-ce4e-4ece-aeff-4f03b20567bf Command: Command for Job #4
    [5] CallerJobID: adaffd34-8c76-48eb-a9e3-dbbf3d7cfd1f Command: Command for Job #5
    [6] CallerJobID: fabc4ae2-681c-44fa-be83-24c185fc6a54 Command: Command for Job #6
    [7] CallerJobID: 7532a8f8-8f97-471a-914b-e1dd9cc88f29 Command: Command for Job #7
    [8] CallerJobID: 433338db-94d3-41c1-a003-f883a57059ca Command: Command for Job #8
    [9] CallerJobID: b1a47e95-c48c-4b36-b6eb-1c582148d564 Command: Command for Job #9
    [10] CallerJobID: 63707629-2140-431b-a91f-13f0b8a37239 Command: Command for Job #10
    [9] caller #1 added d4a1f479-1d27-4b2f-8d9c-5db1fbacf906 to the Q via .JobRequest
    [8] caller #2 added 06a4a886-4054-426b-b963-88e54d4aeda7 to the Q via .JobRequest
    [7] caller #3 added 1415bb08-607c-4bf0-9b28-a1f33eb24c33 to the Q via .JobRequest
    [6] caller #4 added 18568325-ce4e-4ece-aeff-4f03b20567bf to the Q via .JobRequest
    [5] caller #5 added adaffd34-8c76-48eb-a9e3-dbbf3d7cfd1f to the Q via .JobRequest
    [4] caller #6 added fabc4ae2-681c-44fa-be83-24c185fc6a54 to the Q via .JobRequest
    [3] caller #7 added 7532a8f8-8f97-471a-914b-e1dd9cc88f29 to the Q via .JobRequest
    [2] caller #8 added 433338db-94d3-41c1-a003-f883a57059ca to the Q via .JobRequest
    [1] caller #9 added b1a47e95-c48c-4b36-b6eb-1c582148d564 to the Q via .JobRequest
    [0] caller #10 added 63707629-2140-431b-a91f-13f0b8a37239 to the Q via .JobRequest
    ===> Start job ID d4a1f479-1d27-4b2f-8d9c-5db1fbacf906 from caller caller #1
    <===  Stop job ID d4a1f479-1d27-4b2f-8d9c-5db1fbacf906 from caller caller #1
    Result of client jobId: d4a1f479-1d27-4b2f-8d9c-5db1fbacf906 is: "The result of the command Command for Job #1 is 7:27:41 PM"
    ===> Start job ID 06a4a886-4054-426b-b963-88e54d4aeda7 from caller caller #2
    <===  Stop job ID 06a4a886-4054-426b-b963-88e54d4aeda7 from caller caller #2
    Result of client jobId: 06a4a886-4054-426b-b963-88e54d4aeda7 is: "The result of the command Command for Job #2 is 7:27:43 PM"
    ===> Start job ID 1415bb08-607c-4bf0-9b28-a1f33eb24c33 from caller caller #3
    <===  Stop job ID 1415bb08-607c-4bf0-9b28-a1f33eb24c33 from caller caller #3
    Result of client jobId: 1415bb08-607c-4bf0-9b28-a1f33eb24c33 is: "The result of the command Command for Job #3 is 7:27:45 PM"
    ===> Start job ID 18568325-ce4e-4ece-aeff-4f03b20567bf from caller caller #4
    <===  Stop job ID 18568325-ce4e-4ece-aeff-4f03b20567bf from caller caller #4
    Result of client jobId: 18568325-ce4e-4ece-aeff-4f03b20567bf is: "The result of the command Command for Job #4 is 7:27:47 PM"
    ===> Start job ID adaffd34-8c76-48eb-a9e3-dbbf3d7cfd1f from caller caller #5
    <===  Stop job ID adaffd34-8c76-48eb-a9e3-dbbf3d7cfd1f from caller caller #5
    Result of client jobId: adaffd34-8c76-48eb-a9e3-dbbf3d7cfd1f is: "The result of the command Command for Job #5 is 7:27:49 PM"
    ===> Start job ID fabc4ae2-681c-44fa-be83-24c185fc6a54 from caller caller #6
    <===  Stop job ID fabc4ae2-681c-44fa-be83-24c185fc6a54 from caller caller #6
    Result of client jobId: fabc4ae2-681c-44fa-be83-24c185fc6a54 is: "The result of the command Command for Job #6 is 7:27:51 PM"
    ===> Start job ID 7532a8f8-8f97-471a-914b-e1dd9cc88f29 from caller caller #7
    <===  Stop job ID 7532a8f8-8f97-471a-914b-e1dd9cc88f29 from caller caller #7
    Result of client jobId: 7532a8f8-8f97-471a-914b-e1dd9cc88f29 is: "The result of the command Command for Job #7 is 7:27:53 PM"
    ===> Start job ID 433338db-94d3-41c1-a003-f883a57059ca from caller caller #8
    <===  Stop job ID 433338db-94d3-41c1-a003-f883a57059ca from caller caller #8
    Result of client jobId: 433338db-94d3-41c1-a003-f883a57059ca is: "The result of the command Command for Job #8 is 7:27:55 PM"
    ===> Start job ID b1a47e95-c48c-4b36-b6eb-1c582148d564 from caller caller #9
    <===  Stop job ID b1a47e95-c48c-4b36-b6eb-1c582148d564 from caller caller #9
    Result of client jobId: b1a47e95-c48c-4b36-b6eb-1c582148d564 is: "The result of the command Command for Job #9 is 7:27:57 PM"
    ===> Start job ID 63707629-2140-431b-a91f-13f0b8a37239 from caller caller #10
    <===  Stop job ID 63707629-2140-431b-a91f-13f0b8a37239 from caller caller #10
    Result of client jobId: 63707629-2140-431b-a91f-13f0b8a37239 is: "The result of the command Command for Job #10 is 7:27:59 PM"
    done.
    

    ...这次 WITH .Reverse取消注释:

    Loop started
    [1] CallerJobID: 596de3ee-04e2-49dd-aaeb-e4a2aa41c90a Command: Command for Job #1
    [2] CallerJobID: cbf0ab0d-5637-4708-837b-3bf7d605f6e1 Command: Command for Job #2
    [3] CallerJobID: d8ba30c6-7729-4f91-8d86-51dfbf3b4b3f Command: Command for Job #3
    [4] CallerJobID: 292d6212-de33-471a-9fdc-dad8b4bef4d4 Command: Command for Job #4
    [5] CallerJobID: 992af5e4-a713-400c-9792-578ac39c6879 Command: Command for Job #5
    [6] CallerJobID: f87a09fa-8cba-4d56-9658-567f0ef6d63e Command: Command for Job #6
    [7] CallerJobID: fc7a1f0c-1a75-44ce-93bf-dc80d6c810c7 Command: Command for Job #7
    [8] CallerJobID: 01d6af2c-5998-4b38-9244-006e9e12f309 Command: Command for Job #8
    [9] CallerJobID: cae99dd9-e7e2-4614-be5b-a75d944e27bd Command: Command for Job #9
    [10] CallerJobID: 3f02a3bb-5667-48fb-ab22-df8d951b2b13 Command: Command for Job #10
    [9] caller #1 added 596de3ee-04e2-49dd-aaeb-e4a2aa41c90a to the Q via .JobRequest
    [8] caller #2 added cbf0ab0d-5637-4708-837b-3bf7d605f6e1 to the Q via .JobRequest
    [7] caller #3 added d8ba30c6-7729-4f91-8d86-51dfbf3b4b3f to the Q via .JobRequest
    [6] caller #4 added 292d6212-de33-471a-9fdc-dad8b4bef4d4 to the Q via .JobRequest
    [5] caller #5 added 992af5e4-a713-400c-9792-578ac39c6879 to the Q via .JobRequest
    [4] caller #6 added f87a09fa-8cba-4d56-9658-567f0ef6d63e to the Q via .JobRequest
    [3] caller #7 added fc7a1f0c-1a75-44ce-93bf-dc80d6c810c7 to the Q via .JobRequest
    [2] caller #8 added 01d6af2c-5998-4b38-9244-006e9e12f309 to the Q via .JobRequest
    [1] caller #9 added cae99dd9-e7e2-4614-be5b-a75d944e27bd to the Q via .JobRequest
    [0] caller #10 added 3f02a3bb-5667-48fb-ab22-df8d951b2b13 to the Q via .JobRequest
    ===> Start job ID 596de3ee-04e2-49dd-aaeb-e4a2aa41c90a from caller caller #1
    <===  Stop job ID 596de3ee-04e2-49dd-aaeb-e4a2aa41c90a from caller caller #1
    ===> Start job ID cbf0ab0d-5637-4708-837b-3bf7d605f6e1 from caller caller #2
    <===  Stop job ID cbf0ab0d-5637-4708-837b-3bf7d605f6e1 from caller caller #2
    ===> Start job ID d8ba30c6-7729-4f91-8d86-51dfbf3b4b3f from caller caller #3
    <===  Stop job ID d8ba30c6-7729-4f91-8d86-51dfbf3b4b3f from caller caller #3
    ===> Start job ID 292d6212-de33-471a-9fdc-dad8b4bef4d4 from caller caller #4
    <===  Stop job ID 292d6212-de33-471a-9fdc-dad8b4bef4d4 from caller caller #4
    ===> Start job ID 992af5e4-a713-400c-9792-578ac39c6879 from caller caller #5
    <===  Stop job ID 992af5e4-a713-400c-9792-578ac39c6879 from caller caller #5
    ===> Start job ID f87a09fa-8cba-4d56-9658-567f0ef6d63e from caller caller #6
    <===  Stop job ID f87a09fa-8cba-4d56-9658-567f0ef6d63e from caller caller #6
    ===> Start job ID fc7a1f0c-1a75-44ce-93bf-dc80d6c810c7 from caller caller #7
    <===  Stop job ID fc7a1f0c-1a75-44ce-93bf-dc80d6c810c7 from caller caller #7
    ===> Start job ID 01d6af2c-5998-4b38-9244-006e9e12f309 from caller caller #8
    <===  Stop job ID 01d6af2c-5998-4b38-9244-006e9e12f309 from caller caller #8
    ===> Start job ID cae99dd9-e7e2-4614-be5b-a75d944e27bd from caller caller #9
    <===  Stop job ID cae99dd9-e7e2-4614-be5b-a75d944e27bd from caller caller #9
    ===> Start job ID 3f02a3bb-5667-48fb-ab22-df8d951b2b13 from caller caller #10
    <===  Stop job ID 3f02a3bb-5667-48fb-ab22-df8d951b2b13 from caller caller #10
    Result of client jobId: 3f02a3bb-5667-48fb-ab22-df8d951b2b13 is: "The result of the command Command for Job #10 is 7:30:12 PM"
    Result of client jobId: cae99dd9-e7e2-4614-be5b-a75d944e27bd is: "The result of the command Command for Job #9 is 7:30:10 PM"
    Result of client jobId: 01d6af2c-5998-4b38-9244-006e9e12f309 is: "The result of the command Command for Job #8 is 7:30:08 PM"
    Result of client jobId: fc7a1f0c-1a75-44ce-93bf-dc80d6c810c7 is: "The result of the command Command for Job #7 is 7:30:06 PM"
    Result of client jobId: f87a09fa-8cba-4d56-9658-567f0ef6d63e is: "The result of the command Command for Job #6 is 7:30:04 PM"
    Result of client jobId: 992af5e4-a713-400c-9792-578ac39c6879 is: "The result of the command Command for Job #5 is 7:30:02 PM"
    Result of client jobId: 292d6212-de33-471a-9fdc-dad8b4bef4d4 is: "The result of the command Command for Job #4 is 7:30:00 PM"
    Result of client jobId: d8ba30c6-7729-4f91-8d86-51dfbf3b4b3f is: "The result of the command Command for Job #3 is 7:29:58 PM"
    Result of client jobId: cbf0ab0d-5637-4708-837b-3bf7d605f6e1 is: "The result of the command Command for Job #2 is 7:29:56 PM"
    Result of client jobId: 596de3ee-04e2-49dd-aaeb-e4a2aa41c90a is: "The result of the command Command for Job #1 is 7:29:54 PM"
    done.
    

    P.S。是的,我确实使用了“工作”这些术语。和&#39;工作&#39;并且&#39;要求&#39;同义词......这是一个科学怪人...... P.S.S.我没有包括删除包含解决方案的词典条目,一旦它们被选中,并且......直到我收到同行反馈,我才能整理一下...

    我不允许以问号结束解决方案帖子,所以你必须想象它在那里......