2011-07-24 9 views
0

私は、リクエストがキャンセルされ、いくつかの奇妙な結果が見つかったことを調べるためにテストしているカスタム非同期WebRequestクラスを持っています。すぐにキャンセルするには25秒かかりますが、通常のバックグラウンドスレッドを使用した場合はわずか5秒しかかかりませんでした。なぜTask.Startを使用して6 WebRequestをキャンセルすると、Thread.Startよりも時間がかかります

更新:キャンセリングせずに実行すると、Task.Startで9秒、Thread.Startで3秒かかることがあります。スレッドは、すべてのコードによって即座に開始されている間 -

Imports System.Net 
Imports System.Threading 
Imports System.IO 
Imports System.Threading.Tasks 

Module Module2 

Dim closeEvent As New ManualResetEvent(False) 
Dim sw As System.Diagnostics.Stopwatch 

' first domain is invalid 
Dim urls() As String = { 
    "http://www.jbihgcgfxfdxdwewloevedfhvcdfb.com", 
    "http://www.hanselman.com/blog/", 
    "http://www.stackoverflow.com/questions/", 
    "http://www.finderscope.net", 
    "http://msdn.microsoft.com/en-us/library/az24scfc.aspx", 
    "http://www.developerfusion.com/tools/convert/csharp-to-vb/" 
} 

Sub Test1() 
    sw = System.Diagnostics.Stopwatch.StartNew 
    Dim action As Action(Of Object) = Sub(url As String) 
              Dim wReq As WebRequest = WebRequest.Create(url) 
              RunWebStreamAsync(wReq, closeEvent) 
              Console.WriteLine("done in {0} ms : {1}", sw.ElapsedMilliseconds, Left(url, 50)) 
             End Sub 
    Try 
     '' Cosntruct a started task 
     Dim t(5) As task 
     For i As Integer = 0 to 5 
      t(i) = New Task(action, urls(i)) 
     Next 
     For Each tsk In t 
      tsk.Start() 
     Next 
     closeEvent.Set() 
     Task.WaitAll(t) 
    Catch ex As Exception 
     Console.WriteLine(ex.Message) 
    End Try 
    Console.WriteLine("total in {0} ms", sw.ElapsedMilliseconds) 
End Sub 

Dim WaitCount As Integer = 6 
Sub Test2() 
    sw = System.Diagnostics.Stopwatch.StartNew 
    For i As Integer = 0 to 5 
     StartThread(urls(i)) 
    Next 
    closeEvent.Set() 
    Do While WaitCount > 0 
     Thread.Sleep(1000) 
    Loop 
    Console.WriteLine("total in {0} ms", sw.ElapsedMilliseconds) 
End Sub 

Private Sub StartThread(ByVal url As String) 
    Dim trd As Thread = New Thread(AddressOf ThreadTask) 
    trd.IsBackground = True 
    trd.Start(url) 
End Sub 

Private Sub ThreadTask(ByVal arg As Object) 
    Dim url As String = arg 
    Try 
     Dim wReq As WebRequest = WebRequest.Create(url) 
     RunWebStreamAsync(wReq, closeEvent) 
    Catch 
    End Try 
    Console.WriteLine("done in {0} ms : {1}", sw.ElapsedMilliseconds, Left(url, 50)) 
    Interlocked.Decrement(WaitCount) 
End Sub 

Public Sub RunWebStreamAsync(ByVal wr As WebRequest, ByVal CloseTask As ManualResetEvent) 
    Dim hwra As New MyWebRequest 
    hwra.LoadAsync(wr) 
    Do While hwra.AsyncOperationInProgress 
     If CloseTask.WaitOne(1000) = True Then 
      If hwra.CancellationPending = False Then 
       hwra.CancellationPending = True 
       wr.Abort() 
      End If 
     Else 
      Thread.Sleep(100) 
     End If 
    Loop 
End Sub 

Class MyWebRequest 
    Public Property CancellationPending As Boolean 
    Public Property AsyncOperationInProgress As Boolean 

    Public Sub LoadAsync(ByVal req As WebRequest) 
     AsyncOperationInProgress = True 
     ' Invoke BeginGetResponse on a threadpool thread, as it has 
     ' unpredictable latency 
     Dim bgrd = (New WaitCallback(AddressOf Me.BeginGetResponseDelegate)) 
     bgrd.BeginInvoke(req, Nothing, Nothing) 
    End Sub 

    ' Solely for calling BeginGetResponse itself asynchronously. 
    Private Sub BeginGetResponseDelegate(ByVal arg As Object) 
     If CancellationPending Then 
      PostCompleted(Nothing, True) 
     Else 
      Dim req As WebRequest = DirectCast(arg, WebRequest) 
      req.BeginGetResponse(New AsyncCallback(AddressOf GetResponseCallback), req) 
     End If 
    End Sub 

    Private Sub PostCompleted(ByVal p1 As Object, ByVal p2 As Boolean) 
     AsyncOperationInProgress = False 
     ' do something 
    End Sub 

    Private Sub GetResponseCallback(ByVal result As IAsyncResult) 
     If CancellationPending Then 
      PostCompleted(Nothing, True) 
     Else 
      Try 
       ' Continue on with asynchronous reading. 
       PostCompleted(Nothing, True) 
      Catch ex As Exception 
       ' Since this is on a non-UI thread, we catch any exceptions and 
       ' pass them back as data to the UI-thread. 
       PostCompleted(ex, False) 
      End Try 
     End If 
    End Sub 
End Class 
End Module 
+1

'Sleep'と' WaitCount'の代わりに 'Thread.Join'を呼び出す必要があります。 – SLaks

+0

with Thread.Join私は2番目よりも完了するために最初のものを待つだろう、私は独立したかったかった – walter

+3

それはどのような違いがありますか?いずれにせよ、あなたはそれらがすべて完了するのを待っています。 'Join()'を呼び出しても、ターゲットスレッドにはまったく影響しません。それらは並行して実行されます。 – SLaks

答えて

2

違いは、タスクスケジューラは、それが彼らを開始すべきことを決定するとき、あなたのタスクにのみ開始されているという事実によるものです。あなたはすぐにキャンセルする場合は、以下のこのコードはビジーループである

からCloseTaskが設定されている場合、このループは何Sleep -time含まれていません:このコードはCPUサイクルをたくさん取ってループするので

Do While hwra.AsyncOperationInProgress 
     If CloseTask.WaitOne(1000) = True Then 
      If hwra.CancellationPending = False Then 
       hwra.CancellationPending = True 
       wr.Abort() 
      End If 
     Else 
      Thread.Sleep(100) 
     End If 
    Loop 

をタスクスケジューラが新しいタスクを開始しないようにします。そのため、タスクは連続して実行されます(そのため、テストには時間がかかります)。 perfromanceを向上させるために

、あなたが試みることができるいくつかのアイデアがあります:些細なレベルで

  • 、そのビジーループでSleepを置きます。そう
  • CloseTaskイベントが(WR)hwra.LoadAsyncを呼び出す前に設定されている場合は、チェック
  • はCloseTaskが設定されている場合WebRequest.Abortが呼び出されていることを確認し(これはあなたのコードサンプル中で示唆されたが、それは完了しません)
  • 再設計ビジー状態のスレッドを使用する代わりに、完了したイベントを使用してWebリクエストを返すようにします。AsyncOperationInProgressフラグをポーリングします。スレッドを使用して各asyncIOを監視する場合は、同期IOを使用することもできます。
関連する問題