2017-05-20 19 views
0

複数のスレッドを使用してアイテムリストを操作するコンソールアプリケーションがあります。これらのアイテムはすべてAPIにアクセスする必要があり、そのAPIに2回以上リクエストする必要はありません。複数のスレッドが同時に同じ関数にアクセスする

だから私は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 

しかし、どのような2つのスレッドが正確に同じ時間で要求を行う場合、彼らは、起こるを持っていますこれらの両方が最初に実行されるため、両方ともこの行を最初に実行するため、秒が過ぎてチェックされます。

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

この行に移動する

lastRequest = now() 

1秒間に1回のリクエストしかないようにするには、どうすればapiRequest関数を作成できますか?

ソリューション

私は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 

これは、最終的にはモニターが解放されることを保証するために、(Visual Basicでは最後に...試してみてください)ブロック...試みで終了メソッドをラップし、ほぼそのロックとSyncLock除くdbasnettからMonitor.EnterMonitor.Exitanswerと同じように動作します(Alex Bがコメントに指摘されている)

+2

代わりに[ConcurrentQueue](https://msdn.microsoft.com/en-us/library/dd267265(v = vs.110).aspx)に項目を追加し、それらをタイマーティック。あなたはいつでもリクエストを作成できますが、設定したレートで提出され、必要なものはロックされません。 –

答えて

1

これは、1秒あたり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 
+0

ありがとう!これはsynclock APILock/end synclock(これは私がその間に見つけたもので、うまくいくようです)と同じですか? – NLAnaconda

+0

@NLAnaconda - 解決策が見つかった場合はそれを投稿します。 – dbasnett

+0

MSDN:EnterとExitメソッドによって提供される機能は、ロックとSyncLockがExitをラップすることを除けば、C#lockステートメント(Visual BasicではSyncLock)で提供される機能と同じです。メソッドをtry ... finallyブロック(Try ...最終的にはVisual Basic)に置き、モニタが解放されるようにします.' –

0

ConcurrentQueueにアイテムを追加して、それをタイマーティックでデキューできます。あなたはいつでもリクエストを作成できますが、設定したレートで提出され、必要なものはロックされません。例えば

、より多くのアイテムがキューに入れbeiningされたとしても処理されている項目を示し

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 

時々、テキストの色は(ここではillustratableない)ので、間違っていますコンソールの色を設定するスレッドは、テキストが書き込まれる前に中断されます。コンソールにテキストを書き込むことも並行キューには適していますが、このコード例の明快さを妨げるでしょう。

関連する問題