這段時間對網絡爬蟲比較感興趣,實現起來實際上比較簡單。無非就是http的web請求,然后對返回的html內容進行內容篩選。本文的重點不在於這里,而在於多線程做http請求。例如我要實現如下場景:我有N個對象集合,需要通過http的方式獲取每個對象的相關信息。廢話不多說,直接上代碼
實現方式一:依次循環遍歷對象集合,這種方式最為普通
for (int i = 0; i < videoInfoList.Count; i++) { //普通方式 directRun(videoInfoList[i]); } private void directRun(VideoInfo item) { var htmlStr = GetHtmlCode(item.url); item.name= getName(htmlStr); videoInfoQueue.Enqueue(item); } private static string GetHtmlCode(string url) { string htmlCode; HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); webRequest.Timeout = 10000; webRequest.Method = "GET"; webRequest.UserAgent = "Mozilla/4.0"; webRequest.Headers.Add("Accept-Encoding", "gzip, deflate"); try { HttpWebResponse webResponse = (System.Net.HttpWebResponse)webRequest.GetResponse(); using (System.IO.Stream streamReceive = webResponse.GetResponseStream()) { using (var zipStream = new System.IO.Compression.GZipStream(streamReceive, System.IO.Compression.CompressionMode.Decompress)) { using (StreamReader sr = new System.IO.StreamReader(zipStream, Encoding.UTF8)) { htmlCode = sr.ReadToEnd(); } } } } catch { return null; } finally { // 釋放資源 webRequest.Abort(); } return htmlCode; }
實現方式二:使用線程池,使用異步多線程的方式提高效率
在使用線程池的時候一定要注意設置ServicePointManager.DefaultConnectionLimit, 因為默認不設置是2,會導致同時的http請求只能是2個,因為這個問題我自己也卡了很久。使用隊列管理,啟動一個定時器線程,實時刷新顯示獲取到的數 據。實際的開發中,隊列和線程池往往是一對組合出現。至於入隊時候使用鎖的問題,這里可以使用volatile也可以直接使用object鎖,防止入隊出錯
//已經入隊的數目 private int loadingNum = 0; //總數目 private int importNum = 0; //定義隊列 private Queue<VideoInfo> videoInfoQueue = new Queue<VideoInfo>(); //鎖 private object sb = new object(); ServicePointManager.DefaultConnectionLimit = 20; for (int i = 0; i < videoInfoList.Count; i++) { //多線程 ThreadPool.QueueUserWorkItem(multithreadingRun, videoInfoList[i]); Thread.Sleep(1); } private void multithreadingRun(object o) { VideoInfo item = o as VideoInfo; var htmlStr = GetHtmlCode(item.url); item.name = getName(htmlStr); //使用鎖入隊 lock (sb) { videoInfoQueue.Enqueue(item); } } //使用定時器進行出隊顯示 private void Timer1_Tick(object sender, EventArgs e) { if (videoInfoQueue.Count > 0) { VideoInfo item = videoInfoQueue.Dequeue(); label1.Text ++= item.name; } if (loadingNum == importNum) { timer1.Stop(); } }
至此,結束,本文也是我的第一篇博文,歡迎指教!
