我有一个控制台应用程序,它使用多个线程来处理项目列表。所有这些项目都需要访问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
这与来自answer的Monitor.Enter
,Monitor.Exit
dbasnett几乎相同,只是锁和SyncLock将Exit方法包装在try ... finally块中(尝试...最后在Visual Basic中)确保监视器被释放(在评论中指出Alex B)
答案 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
有时文本的颜色(此处不可插图)是错误的,因为设置控制台颜色的线程在写入文本之前被中断。将文本写入控制台也是一个很好的候选对象,但它会妨碍这个示例代码的清晰度。