我注意到我为正在处理的应用程序创建的线程越多,我的GUI就越开始冻结.我不确定为什么会发生这种情况,我以前认为多线程应用程序的一部分原因是为了避免GUI冻结.
一个解释将不胜感激.
另外,这是我用来创建线程的代码:
private void runThreads(int amount,ThreadStart address) { for (int i = 0; i < amount; i++) { threadAmount += 1; Thread currentThread = new Thread(address); currentThread.Start(); } }
这是线程运行的内容:
private void checkProxies() { while (started) { try { WebRequest request = WebRequest.Create("http://google.co.nz/"); request.Timeout = (int)timeoutCounter.Value * 1000; request.Proxy = new WebProxy(proxies[proxyIndex]); Thread.SetData(Thread.GetNamedDataSlot("currentProxy"),proxies[proxyIndex]); if (proxyIndex != proxies.Length) { proxyIndex += 1; } else { started = false; } request.GetResponse(); workingProxies += 1; } catch (WebException) { deadProxies += 1; } lock ("threadAmount") { if (threadAmount > proxies.Length - proxyIndex) { threadAmount -= 1; break; } } } }
解决方法
>创建Thread对象很昂贵.这就是为什么在C#中添加新类以更好地处理多线程的原因.现在您可以访问Task类或Parallel类(如下所述).
>从评论来看,你同时运行了很多线程.虽然运行它们不应该是一个问题,但如果你正在做的是激活WebRequests(除非你有一个很棒的网络),你实际上并没有多少使用它们.确实使用多个线程,但限制其数量.
>当您想在后台执行特定操作时,任务很棒.但是当你想在后台重复一个特定数据集的单个操作时…为什么不使用System.Threading.Tasks.Parallel类?具体来说,Parallel.ForEach(您可以在其中指定代理列表作为参数).此方法还允许您使用ParallelOptions设置在任何给定时刻应该同时运行的线程数.
>另一种代码编写方法是使用.NET 4.5中提供的async和await关键字.在这种情况下,你的GUI(按下按钮?)应该调用异步方法.
>使用Interlocked.Increment或Interlocked.Add等线程安全方法来增加/减少多个线程可用的计数器.另外,请考虑您可以将代理列表更改为ConcurrentDictionary< string,bool> (其中bool表示代理是否有效)并且无需担心设置值,因为每个线程只能访问字典中自己的条目.例如,您可以使用LINQ:dictionary.Where(q => q.Value).Count()轻松地对总计进行排队,以获取工作代理的数量.当然还有其他类可用,具体取决于你想要解决的问题 – 也许是Queue(或ConcurrentQueue)?
>你的锁不应该真正起作用……因为它似乎是偶然的,而不是你的代码中的设计(感谢Luaan的评论).但你真的不应该这样做.请查阅MSDN documentation锁定以更好地了解它的工作原理.在MSDN示例中创建的对象不仅仅用于show.
>您还可以使用BeginGetResponse和EndGetResponse方法使请求本身成为多线程.实际上,您可以将它与Task类结合使用,以获得更清晰的代码(Task类可以将Begin / End方法对转换为单个Task对象).
因此,快速回顾 – 使用Parallel类进行多线程,并使用并发类来保持适当的位置.
这是我写的一个简单例子:
private ConcurrentDictionary<string,bool?> values = new ConcurrentDictionary<string,bool?>(); private async void Button_Click(object sender,RoutedEventArgs e) { var result = await CheckProxies(); label.Content = result.ToString(); } async Task<int> CheckProxies() { //I don't actually HAVE a list of proxies,so I make up some data for (int i = 0; i < 1000; i++) values[Guid.NewGuid().ToString()] = null; await Task.Factory.StartNew(() => Parallel.ForEach(values,new ParallelOptions() { MaxDegreeOfParallelism = 10 },this.PeformOperation)); //note that with maxDegreeOfParallelism set to a high value (like 1000) //then I'll get a TON of Failed requests simply because I'm overloading the network //either that or google thinks I'm DDOSing them... >_< return values.Where(v => v.Value == true).Count(); } void PeformOperation(KeyValuePair<string,bool?> kvp) { try { WebRequest request = WebRequest.Create("http://google.co.nz/"); request.Timeout = 100; //I'm not actually setting up the proxy from kvp,//because it's populated with bogus data request.GetResponse(); values[kvp.Key] = true; } catch (WebException) { values[kvp.Key] = false; } }