2

ネットワークに過負荷をかけずに、大量のWebリクエストを行う必要があることがよくあります

私は現在、ThreadPool.SetMinThreadsとMaxDegreeOfParallelismを利用して同期リクエストを並行して実行し、同時に実行されるリクエストの数を正確に指定することでこれを行っています。

これで問題なく動作しますが、気分が悪くなります。

本当に非同期メソッドを利用したいのですが、同時リクエスト数を制限する方法がわかりません。

これを行う並列方法の簡単な例(Webクライアントを使用し、簡潔にするためにエラー処理を行わない):

Private Function SearchSitesForKeywordInParallel(ByVal keyword As String, ByVal sites As String(), ByVal maxConcurrency As Integer) As String()
    Dim po As New ParallelOptions
    po.MaxDegreeOfParallelism = maxConcurrency
    Threading.ThreadPool.SetMinThreads(maxConcurrency, 2)
    Dim sitesContainingKeyword As New Concurrent.ConcurrentBag(Of String)

    Parallel.For(0, sites.Count, po, Sub(i)
                                         Dim wc As New Net.WebClient
                                         wc.Proxy = Nothing
                                         Dim pageSource As String = wc.DownloadString(sites(i))
                                         If pageSource.Contains(keyword) Then
                                             sitesContainingKeyword.Add(sites(i))
                                         End If
                                     End Sub)
    Return sitesContainingKeyword.ToArray
End Function

これはブロッキング機能であり、私が必要としているものです。これで、通常のforループでwebclient.downloadStringAsyncメソッドをテストしました。これにより、すべてのリクエストがほぼ一度に発生し、ネットワークが過負荷になります。

私がやりたいのは、最初にXリクエストを作成し、次に各応答が返されるたびに新しいリクエストを作成することです。

私はタスクが進むべき道であるとかなり確信しています、そして私はc#のいくつかの非常に素晴らしい実装を読んだことを確信していますが、私のc#の経験は限られており、c#ランバダをvb.netに変換するのに苦労しています。

私もvs2010と.net4に制限されているので、.net4.5asyncawaitの優れた点は私にとって選択肢ではありません。

どんな助けでも大歓迎です

4

3 に答える 3

1

完全に理解していれば、何を達成したいのかわかりませんが、ayncメソッドを使用したい場合は、次のように実行できます。

    Dim google As String = "http://www.google.com/#&q="

    Dim qsites As New Concurrent.ConcurrentQueue(Of String)
    For Each k In {"foo", "bar", "john", "jack", "stackoverflow", "basic", "ship", "car", "42"}
        qsites.Enqueue(google & k)
    Next

    Dim cde As New System.Threading.CountdownEvent(qsites.Count)

    Dim strings As New Concurrent.ConcurrentBag(Of String)
    Dim completedhandler = Sub(wco As Object, ev As Net.DownloadStringCompletedEventArgs)
                               Dim wc = DirectCast(wco, Net.WebClient)
                               Debug.Print("got one!")
                               strings.Add(ev.Result)
                               cde.Signal()
                               Dim s As String = String.Empty
                               If qsites.TryDequeue(s) Then
                                   Debug.Print("downloading from {0}", s)
                                   wc.DownloadStringAsync(New Uri(s))
                               End If
                           End Sub

    Dim numthreads As Integer = 4

    System.Threading.Tasks.Task.Factory.StartNew(Sub()
                                                     For i = 1 To numthreads
                                                         Dim s As String = String.Empty
                                                         If qsites.TryDequeue(s) Then
                                                             Dim wc As New Net.WebClient
                                                             wc.Proxy = Nothing
                                                             AddHandler wc.DownloadStringCompleted, completedhandler
                                                             Debug.Print("downloading from {0}", s)
                                                             wc.DownloadStringAsync(New Uri(s))
                                                         End If
                                                     Next
                                                 End Sub)

    cde.Wait()

(afaik)WCのdownloadcompletedイベントがUIスレッド(またはcurrentsync..context)で発生し、cde.waitがイベントの処理を許可しないため、別のスレッド/タスクで非同期ダウンロードを「開始」するだけで済みます。 。

于 2012-12-18T15:30:37.557 に答える
1

最近同様の問題を解決したので、これに別の答えを追加したいと思います(コードスニペットはC#であることに注意してくださいが、アイデアを与える必要があります)。

以前は、異なるスレッドのhttpサーバーに送信される並列http同期リクエストの数があり、セマフォを使用して送信するリクエストの数を制限していました。

今、私は新しいTPL(c#5.0-aysn / await-非常に便利です(基本的にTPLで導入された継続は私にとって自然な音です-そしてasync / awaitを使用するとはるかに使いやすくなりました))に適応し、ネットワークI/Oを呼び出します非同期的に。

つまり、理想的には、呼び出し元で1つのスレッドのみを使用し(続行する前に実際に結果を取得する必要がある場合を除く)、. net、os、およびI / o完了ポートスレッドを連携させて、スレッドプール内の継続コードを呼び出して操作を完了します(基本的に、APMの「コールバック」、イベントベースのパターンで完了したイベント、TPLの「継続」、C#5.0(4.5 .net)で待機した後のコード)

非同期I/Oを採用したときに従った原則は単純です。本当に必要な場合を除いて、スレッドを待たせてCPUとリソースを浪費しないでください。

于 2014-06-26T21:37:51.543 に答える
0

これは、VB.NETで、NuGetから取得できるWintellectPowerthreadingライブラリのAsyncEnumeratorクラスを使用して非同期で実行できます。

これにより、Awaitの機能の一部が提供されますが、VS2010では.Net 2.0から4.0で動作し、4.5非同期機能へのアップグレードパスが提供されます。

欠点は、WebClient非同期メソッドがAsyncEnumeratorで使用されるTask <>に基づくEAPからAPMへのシムを必要とするため、コードが非常に複雑になることです。

同時リクエストの数を制御する最も簡単な方法は、X非同期操作を開始し、完了するたびに別の操作を開始することです。

コード例:

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Imports System.Threading.Tasks
Imports System.Net
Imports Wintellect.Threading.AsyncProgModel

Module TaskExtension
    REM http://msdn.microsoft.com/en-us/library/hh873178.aspx
    <Extension()>
    Public Function AsApm(Of T1)(ByVal task As Task(Of T1), callback As AsyncCallback, state As Object) As IAsyncResult
        If (task Is Nothing) Then
            Throw New ArgumentNullException("task")
        End If
        Dim tcs = New TaskCompletionSource(Of T1)(state)
        task.ContinueWith(Sub(t As Task(Of T1))
                              If (t.IsFaulted) Then
                                  tcs.TrySetException(t.Exception.InnerExceptions)
                              ElseIf t.IsCanceled Then
                                  tcs.TrySetCanceled()
                              Else : tcs.TrySetResult(t.Result)
                              End If
                              If (Not callback Is Nothing) Then
                                  callback(tcs.Task)
                              End If
                          End Sub, TaskScheduler.Default)
        Return tcs.Task
    End Function
End Module

Module ApmAsyncDownload
    Public Function DownloadStringAsync(url As Uri) As Task(Of String)
        Dim tcs As New TaskCompletionSource(Of String)
        Dim wc As New WebClient()
        AddHandler wc.DownloadStringCompleted, Sub(s As Object, e As System.Net.DownloadStringCompletedEventArgs)
                                                   If (Not (e.Error Is Nothing)) Then
                                                       tcs.TrySetException(e.Error)
                                                   ElseIf e.Cancelled Then
                                                       tcs.TrySetCanceled()
                                                   Else : tcs.TrySetResult(e.Result)
                                                   End If
                                               End Sub
        wc.DownloadStringAsync(url)
        Return tcs.Task
    End Function
    Public Function BeginDownloadString(url As Uri, callback As AsyncCallback, state As Object) As IAsyncResult
        Return DownloadStringAsync(url).AsApm(callback, state)
    End Function
    Public Function EndDownloadString(asyncResult As IAsyncResult) As String
        Dim castToTask As Task(Of String) = asyncResult
        Return castToTask.Result
    End Function
End Module

Public Class AsyncIterators
    Private Shared Iterator Function SearchUrl(ae As AsyncEnumerator(Of Boolean), keyword As String, uri As Uri) As IEnumerator(Of Int32)
        ae.Result = False
        ApmAsyncDownload.BeginDownloadString(uri, ae.End(0, AddressOf ApmAsyncDownload.EndDownloadString), Nothing)
        Yield 1
        If (ae.IsCanceled()) Then
            Return
        End If
        Try
            Dim page As String = ApmAsyncDownload.EndDownloadString(ae.DequeueAsyncResult)
            ae.Result = page.Contains(keyword)
        Catch ex As AggregateException
        End Try
    End Function
    Public Shared Iterator Function SearchIterator(ae As AsyncEnumerator(Of List(Of String)), keyword As String, urls As List(Of Uri)) As IEnumerator(Of Int32)
        ae.Result = New List(Of String)
        'Control how many searches are started asynchonously
        Dim startSearches = Math.Min(3, urls.Count)
        Dim enumerator = urls.GetEnumerator
        Dim toBeCompleted = urls.Count
        Do Until (toBeCompleted <= 0)
            While (startSearches > 0)
                If enumerator.MoveNext Then
                    Dim subAe = New AsyncEnumerator(Of Boolean)()
                    subAe.SyncContext = Nothing
                    subAe.BeginExecute(SearchUrl(subAe, keyword, enumerator.Current), ae.End(0, Function(ar As IAsyncResult) As AsyncEnumerator.EndObjectXxx
                                                                                                    subAe.EndExecute(ar)
                                                                                                End Function), enumerator.Current)
                End If
                startSearches = startSearches - 1
            End While
            'Wait for first async search to complete
            Yield 1
            toBeCompleted = toBeCompleted - 1
            If (ae.IsCanceled()) Then
                Exit Do
            End If
            'Get result of the search and add to results
            Dim result = ae.DequeueAsyncResult()
            Dim completedAe = AsyncEnumerator(Of Boolean).FromAsyncResult(result)
            If (completedAe.EndExecute(result)) Then
                Dim uri As Uri = result.AsyncState
                ae.Result.Add(uri.OriginalString)
            End If
            'Start 1 more search
            startSearches = startSearches + 1
        Loop
    End Function
End Class

Module Module1
    Sub Main()
        Dim searchAe = New AsyncEnumerator(Of List(Of String))()
        searchAe.SyncContext = Nothing
        Dim urlStrings = New List(Of String) From {"http://www.google.com", "http://www.yahoo.com", "http://www.dogpile.com"}
        Dim uris = urlStrings.Select(Function(urlString As String) As Uri
                                         Return New Uri(urlString)
                                     End Function).ToList()
        For Each Str As String In searchAe.EndExecute(searchAe.BeginExecute(AsyncIterators.SearchIterator(searchAe, "search", uris), Nothing, Nothing))
            Console.WriteLine(Str)
        Next
        Console.ReadKey()
    End Sub
End Module

そして、c#ラムダの翻訳についてあなたが何を意味するのかがわかりました!

于 2012-12-17T19:10:28.267 に答える