在多重的WebRequest的管理更好的方法方法、WebRequest

2023-09-04 01:12:57 作者:眼红了耶

我有一个处理每个单独的线程多个Web请求的组件。每个WebRequest的处理是同步的。

I have an component that is processing multiple web requests each in separate thread. Each WebRequest processing is synchronous.

public class WebRequestProcessor:System.ComponentModel.Component
{
    List<Worker> tlist = new List<Worker>();
    public void Start()
    {
        foreach(string url in urlList){
            // Create the thread object. This does not start the thread.
            Worker workerObject = new Worker();
            Thread workerThread = new Thread(workerObject.DoWork);

            // Start the worker thread.
            workerThread.Start(url);
            tlist.Add(workerThread);
        }
    }
}

public class Worker
{
    // This method will be called when the thread is started.
    public void DoWork(string url)
    {
        // prepare the web page we will be asking for
        HttpWebRequest  request  = (HttpWebRequest) 
            WebRequest.Create(url);

        // execute the request
        HttpWebResponse response = (HttpWebResponse)
            request.GetResponse();

        // we will read data via the response stream
        Stream resStream = response.GetResponseStream();

        // process stream
    }
}

现在我必须找到最佳的方式如何取消所有的请求。

Now I have to find optimal way how to cancel all requests.

的一种方式是向每个同步的WebRequest转换成异步并使用WebRequest.Abort来取消处理。

One way is to convert each synchronous WebRequest into async and use WebRequest.Abort to cancel processing.

另一种方式是释放线程的指针,让所有线程使用GC死。

Another way is to release thread pointers and allow all threads to die using GC.

推荐答案

如果你想下载1000个文件,从1000线程一次肯定不是最好的选择。它不仅可能不会给你任何的加速用时在同一时间下载只是几个文件进行比较,它也将需要至少1 GB的虚拟内存。创建线程是昂贵的,尽量避免在循环中这样做。

If you want to download 1000 files, starting 1000 threads at once is certainly not the best option. Not only it probably won't get you any speedup when compared with downloading just a few files at a time, it will also require at least 1 GB of virtual memory. Creating threads is expensive, try to avoid doing so in a loop.

你应该做的是使用 Parallel.ForEach()随请求和响应操作的异步版本。例如像这样(WPF code):

What you should do instead is to use Parallel.ForEach() along with the asynchronous versions of the request and response operations. For example like this (WPF code):

private void Start_Click(object sender, RoutedEventArgs e)
{
    m_tokenSource = new CancellationTokenSource();
    var urls = …;
    Task.Factory.StartNew(() => Start(urls, m_tokenSource.Token), m_tokenSource.Token);
}

private void Cancel_Click(object sender, RoutedEventArgs e)
{
    m_tokenSource.Cancel();
}

void Start(IEnumerable<string> urlList, CancellationToken token)
{
    Parallel.ForEach(urlList, new ParallelOptions { CancellationToken = token },
                     url => DownloadOne(url, token));

}

void DownloadOne(string url, CancellationToken token)
{
    ReportStart(url);

    try
    {
        var request = WebRequest.Create(url);

        var asyncResult = request.BeginGetResponse(null, null);

        WaitHandle.WaitAny(new[] { asyncResult.AsyncWaitHandle, token.WaitHandle });

        if (token.IsCancellationRequested)
        {
            request.Abort();
            return;
        }

        var response = request.EndGetResponse(asyncResult);

        using (var stream = response.GetResponseStream())
        {
            byte[] bytes = new byte[4096];

            while (true)
            {
                asyncResult = stream.BeginRead(bytes, 0, bytes.Length, null, null);

                WaitHandle.WaitAny(new[] { asyncResult.AsyncWaitHandle,
                                           token.WaitHandle });

                if (token.IsCancellationRequested)
                    break;

                var read = stream.EndRead(asyncResult);

                if (read == 0)
                    break;

                // do something with the downloaded bytes
            }
        }

        response.Close();
    }
    finally
    {
        ReportFinish(url);
    }
}

这样,当您取消操作,所有下载都被取消,并没有新的启动。此外,您可能需要设置 MaxDegreeOfParallelism ParallelOptions ,让你没有做太多的下载一次。

This way, when you cancel the operation, all downloads are canceled and no new ones are started. Also, you probably want to set MaxDegreeOfParallelism of ParallelOptions, so that you aren't doing too many downloads at once.

我不知道你怎么想与你正在下载的文件,因此,使用的StreamReader 可能是一个更好的选择。

I'm not sure what do you want to do with the files you are downloading, so using StreamReader might be a better option.