以下异步C#代码通过7个URL列表运行,并尝试从每个URL获取HTML。现在我只是将它输出到控制台的简单调试响应,如“站点HTML”,“无响应”或“错误的URL”。它似乎工作正常,但我需要在所有7个查询完成后触发一个事件。我该怎么做?重要的是要考虑所有案例:1)已收到网站HTML,2)网站超时,3)网站是一个不正确的网址,无法加载。我已经涵盖了所有这些案例,但无法弄清楚如何连接所有内容以触发全局“OnComplete”事件。
谢谢。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Timers;
using System.Collections.Concurrent;
using System.Diagnostics;
namespace AsyncApp_05
{
class Program
{
static int _count = 0;
static int _total = 0;
static void Main(string[] args)
{
ArrayList alSites = new ArrayList();
alSites.Add("http://www.google.com");
alSites.Add("http://www.yahoo.com");
alSites.Add("http://www.ebay.com");
alSites.Add("http://www.aol.com");
alSites.Add("http://www.bing.com");
alSites.Add("adsfsdfsdfsdffd");
alSites.Add("http://wwww.fjasjfejlajfl");
alSites.Add("http://mundocinema.com/noticias/the-a-team-2/4237");
alSites.Add("http://www.spmb.or.id/?p=64");
alSites.Add("http://gprs-edge.ru/?p=3");
alSites.Add("http://blog.tmu.edu.tw/MT/mt-comments.pl?entry_id=3141");
_total = alSites.Count;
//Console.WriteLine(_total);
ScanSites(alSites);
Console.Read();
}
private static void ScanSites(ArrayList sites)
{
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
}
static void ReadCallback(IAsyncResult result)
{
try
{
// Get RequestState
RequestState state = (RequestState)result.AsyncState;
// determine how many bytes have been read
int bytesRead = state.ResponseStream.EndRead(result);
if (bytesRead > 0) // stream has not reached the end yet
{
// append the read data to the ResponseContent and...
state.ResponseContent.Append(Encoding.ASCII.GetString(state.BufferRead, 0, bytesRead));
// ...read the next piece of data from the stream
state.ResponseStream.BeginRead(state.BufferRead, 0, state.BufferSize,
new AsyncCallback(ReadCallback), state);
}
else // end of the stream reached
{
if (state.ResponseContent.Length > 0)
{
Console.WriteLine("Site HTML");
// do something with the response content, e.g. fill a property or fire an event
//AsyncResponseContent = state.ResponseContent.ToString();
// close the stream and the response
state.ResponseStream.Close();
state.Response.Close();
//OnAsyncResponseArrived(AsyncResponseContent);
}
}
}
catch (Exception ex)
{
// Error handling
RequestState state = (RequestState)result.AsyncState;
if (state.Response != null)
{
state.Response.Close();
}
}
}
static void ResponseCallback(IAsyncResult result)
{
Interlocked.Increment(ref _count);
Console.WriteLine("Count: " + _count);
try
{
// Get and fill the RequestState
RequestState state = (RequestState)result.AsyncState;
HttpWebRequest request = state.Request;
// End the Asynchronous response and get the actual resonse object
state.Response = (HttpWebResponse)request.EndGetResponse(result);
Stream responseStream = state.Response.GetResponseStream();
state.ResponseStream = responseStream;
// Begin async reading of the contents
IAsyncResult readResult = responseStream.BeginRead(state.BufferRead, 0, state.BufferSize, new AsyncCallback(ReadCallback), state);
}
catch (Exception ex)
{
// Error handling
RequestState state = (RequestState)result.AsyncState;
if (state.Response != null)
{
state.Response.Close();
}
Console.WriteLine("No Response");
}
}
static void TimeOutCallback(object state, bool timedOut)
{
if (timedOut)
{
HttpWebRequest request = state as HttpWebRequest;
if (request != null)
{
request.Abort();
}
}
}
}
public class RequestState
{
public int BufferSize { get; private set; }
public StringBuilder ResponseContent { get; set; }
public byte[] BufferRead { get; set; }
public HttpWebRequest Request { get; set; }
public HttpWebResponse Response { get; set; }
public Stream ResponseStream { get; set; }
public RequestState()
{
BufferSize = 1024;
BufferRead = new byte[BufferSize];
ResponseContent = new StringBuilder();
Request = null;
ResponseStream = null;
}
}
}
答案 0 :(得分:0)
恕我直言创建信号量的最简单方法是,在主线程中将每个OnComplete
处理程序设置为Release
和WaitOne
N次(其中N是网站数量) )。
private static void ScanSites(ArrayList sites)
{
var semaphore = new Semaphore(0,sites.Count);
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
state.Semaphore = semaphore;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
(o, timeout => { TimeOutCallback }, request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
for(var i =0; i <sites.Count; i++) semaphore.WaitOne();
}
static void ReadCallback(IAsyncResult result)
{
try
{ ... }
finally{
var state = result.State as RequestState;
if (state != null) state.Semaphore.Release();
}
}
另一个选择是将一些WaitHandle
(ManualResetEvent
适合)传递给每个处理程序,并将WaitHandle.WaitAll
传递给主线程中的<{p>}。
private static void ScanSites(ArrayList sites)
{
var handles = new List<WaitHandle>();
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
handles.Add(result.AsyncWaitHandle);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
WaitHandle.WaitAll(handles.ToArray());
}
当然,您也可以通过使用例如Interlocked
来实现相同的目标。 Exchange
或CompareExchange
方法,但恕我直言,WaitHandle
在这里更直接(使用它们的性能不高)。
答案 1 :(得分:0)
您可以使用CountdownEvent查找所有网站的扫描时间。它最初设置为sites.Count,然后等待该事件。在每次完成时(通过错误,超时或成功),您都会发出事件信号。当事件计数达到零时,等待将返回,您可以进行“OnComplete”事件。