多个线程同时访问相同的功能

时间:2017-05-20 14:14:35

标签: .net vb.net multithreading

我有一个控制台应用程序,它使用多个线程来处理项目列表。所有这些项目都需要访问API,而我不想每秒向该API发出超过1个请求。

所以我有一个执行API请求的类

Public Class apiRequest
  Property lastRequest as date = now()

  function doRequest()
      'check if we can make a request
      if Now.Substract(lastRequest).Seconds > 1 then
         lastRequest = now()

         'Do request [...]
      end if
  end function
End Class

我有主程序

property apiRequester as new apiRequest

sub main
    'start multiple threads (addressOf threadFunction)[...]
end sub

sub threadFunction()
  dim data as string = apirequester.doRequest() 'Call the api request
end sub

但是如果两个线程同时完成一个请求会发生什么,它们都会经过de秒检查,因为它们先执行这一行

if Now.Substract(lastRequest).Seconds > 1 then

在任何人到达此行之前

 lastRequest = now()

如何制作apiRequest函数,以便每秒只有1个请求?

解决方案

我使用SyncLock找到了以下解决方案。

Private locker As New Object
Private lastRequest as date = now()

function doRequest()

  SyncLock locker
    if Now.Substract(lastRequest).Seconds > 1 then
      lastRequest = now()
    end if
  End Synclock

end function

这与来自answerMonitor.EnterMonitor.Exit dbasnett几乎相同,只是锁和SyncLock将Exit方法包装在try ... finally块中(尝试...最后在Visual Basic中)确保监视器被释放(在评论中指出Alex B

2 个答案:

答案 0 :(得分:1)

这将禁止每秒发生多个请求。它假定这个

dim data as string = apirequester.doRequest() 'Call the api request

想要等待其他请求。你的班级被修改了。

Public Class apiRequest
    Private APILock As New Object
    Private stpw As Stopwatch = Stopwatch.StartNew
    Private Shared ReadOnly ReqEvery As New TimeSpan(0, 0, 1)

    Function doRequest() As Boolean
        'check if we can make a request
        Threading.Monitor.Enter(Me.APILock) 'only one request past this point at a time
        Do While Me.stpw.Elapsed < ReqEvery 'loop until one second has passed
            Threading.Thread.Sleep(10)
        Loop

        'Do request [...]

        Me.stpw.Restart() 'restart the clock
        Threading.Monitor.Exit(Me.APILock) 'allow other requests
    End Function
End Class

答案 1 :(得分:0)

您可以将项目添加到ConcurrentQueue并在计时器滴答中将其排队。这样您就可以随时创建请求,但它们将以您设置的速率提交,并且不会锁定所需的任何内容。

例如,

Option Infer On

Imports System.Collections.Concurrent
Imports System.Timers

Module Module1

    Class ApiRequest
        Property SerialNo As Integer
    End Class

    Dim q As ConcurrentQueue(Of ApiRequest)
    Dim tim As Timer

    Sub Dequeue(sender As Object, e As ElapsedEventArgs)
        Dim ar As ApiRequest = Nothing
        If q.TryDequeue(ar) Then
            Console.ForegroundColor = ConsoleColor.Green
            Console.WriteLine($"Processed: {ar.SerialNo}")
        End If

    End Sub

    Sub Init()
        q = New ConcurrentQueue(Of ApiRequest)
        tim = New Timer With {.AutoReset = True, .Interval = 1000}
        AddHandler tim.Elapsed, AddressOf Dequeue
        tim.Start()

    End Sub

    Sub Main()
        Init()

        For i = 1 To 20
            Dim ar = New ApiRequest With {.SerialNo = i}
            q.Enqueue(ar)
            Console.ForegroundColor = ConsoleColor.Red
            Console.WriteLine($"Created: {ar.SerialNo}")
            Threading.Thread.Sleep(200)
        Next

        Console.ForegroundColor = ConsoleColor.Yellow
        Console.WriteLine("<press any key to exit>")

        ' wait for user
        Console.Read()

        ' clean up
        tim.Stop()
        RemoveHandler tim.Elapsed, AddressOf Dequeue
        tim.Dispose()

    End Sub

End Module

示例输出显示正在处理的项目,即使更多项目正在排队:

Created: 1
Created: 2
Created: 3
Created: 4
Created: 5
Created: 6
Processed: 1
Created: 7
Created: 8
Created: 9
Created: 10
Processed: 2
Created: 11
Created: 12
Created: 13
Created: 14
Created: 15
Processed: 3
Created: 16
Created: 17
Created: 18
Created: 19
Created: 20
Processed: 4
<press any key to exit>
Processed: 5
Processed: 6
Processed: 7
Processed: 8
Processed: 9
Processed: 10
Processed: 11
Processed: 12
Processed: 13
Processed: 14
Processed: 15
Processed: 16
Processed: 17
Processed: 18
Processed: 19
Processed: 20

有时文本的颜色(此处不可插图)是错误的,因为设置控制台颜色的线程在写入文本之前被中断。将文本写入控制台也是一个很好的候选对象,但它会妨碍这个示例代码的清晰度。