我正在测试自定义异步WebRequest类,以了解请求被取消的速度有多快,并发现一些奇怪的结果,用TPL启动6个WebRequests并立即取消它需要25秒,但是当我使用时只需要背景线程只花了5秒。
更新:在没有取消的情况下运行它们需要使用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
答案 0 :(得分:2)
不同之处在于,您的任务仅在任务计划程序决定应该启动它们时启动 - 而线程全部由您的代码立即启动。
如果你立即取消,下面这段代码是一个繁忙的循环 - 设置CloseTask
时,这个循环不包含Sleep
- 时间:
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
由于此代码循环占用大量CPU周期,因此这将阻止任务调度程序启动新任务 - 这就是为什么任务会串行执行(因此为什么测试需要这么长时间)。
为了提高性能,您可以尝试的一些想法是:
Sleep
置于那个繁忙的循环中。WebRequest.Abort
(这在代码示例中暗示,但它没有完成)AsyncOperationInProgress
标志 - 如果您使用线程来监视每个asyncIO,那么您也可以使用Sync IO。