所以我不是最熟练的C#编程语言,不过我已经在这里和那里做了一些测试应用程序。
我注意到我为正在处理的应用程序创建的线程越多,我的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;
}
}
}
}
答案 0 :(得分:5)
虽然我无法告诉您为什么您的代码确实会降低GUI的速度,但您应该在代码中执行一些操作以使其更好地全面运行。如果问题仍然存在,那么找出问题要容易得多。
Thread
个对象非常昂贵。这就是为什么在C#中添加新类以更好地处理多线程的原因。现在您可以访问Task
类或Parallel
类(如下所述)。WebRequests
,那么您实际上并没有多少使用它们(除非您有一个很棒的网络)。当然,请使用多个线程,但要限制它们的数量。Task
非常棒。但是当你想在后台为一组特定的数据重复一次操作时......为什么不使用System.Threading.Tasks.Parallel
类?具体来说,Parallel.ForEach
(您可以在其中指定代理列表作为参数)。此方法还允许您使用ParallelOptions
设置在任何给定时刻应同时运行的线程数。async
和await
关键字。在这种情况下,您的GUI(按下按钮?)应该调用async
方法。Interlocked.Increment
或Interlocked.Add
等线程安全方法来增加/减少多个线程可用的计数器。另外,请考虑您可以将代理列表更改为ConcurrentDictionary<string, bool>
(其中bool
表示代理是否正常工作)并设置值而不必担心,因为每个线程只会访问自己的条目词典。例如,您可以使用LINQ:dictionary.Where(q => q.Value).Count()
轻松地对总计进行排队,以获取工作代理的数量。当然也可以使用其他课程,具体取决于您想要解决的问题 - 也许是Queue
(或ConcurrentQueue
)?lock
不应该真正起作用......因为它似乎是偶然的,而不是你的代码中的设计(感谢Luaan的评论)。但你真的不应该这样做。请参阅lock
上的MSDN documentation,以便更好地了解其工作原理。 MSDN示例中创建的Object
不仅适用于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;
}
}
答案 1 :(得分:3)
尽管其他评论是正确的,您应该使用Task
类,或者更好的async
API,这不是您锁定的原因。
导致线程锁定的代码行是:
request.Timeout = (int)timeoutCounter.Value * 1000;
我假设timeoutCounter
是WinForm的控件 - 在主GUI线程上运行。
换句话说,你的线程&#39;代码试图访问一个不属于它自己的线程的控件,这个控件实际上不是“允许的”,至少不那么简单。
例如,this question显示了如何执行此操作,尽管大多数答案都有点过时了。
从一个快速谷歌(好吧,我开玩笑,我把它打包)我发现this article可以很好地解释问题。