ConcurrentStack的问题在 .net c#4 中
此类旨在获取 url 列表,扫描它们,然后返回不起作用的列表。它使用多个线程来避免在长列表上花费太多时间。
我的问题是,即使我用在所有 url 上返回失败的测试函数替换实际的 url 扫描,该类也会返回可变数量的失败。
我假设我的问题出在 ConcurrentStack.TryPop() 或 .Push() 上,但我一生都无法弄清楚为什么。他们应该是线程安全的,我也尝试过锁定,但没有帮助。
谁能向我解释我做错了什么?我对多线程没有太多经验。
public class UrlValidator
{
private const int MAX_THREADS = 10;
private List<Thread> threads = new List<Thread>();
private ConcurrentStack<string> errors = new ConcurrentStack<string>();
private ConcurrentStack<string> queue = new ConcurrentStack<string>();
public UrlValidator(List<string> urls)
{
queue.PushRange(urls.ToArray<string>());
}
public List<string> Start()
{
threads = new List<Thread>();
while (threads.Count < MAX_THREADS && queue.Count > 0)
{
var t = new Thread(new ThreadStart(UrlWorker));
threads.Add(t);
t.Start();
}
while (queue.Count > 0) Thread.Sleep(1000);
int runningThreads = 0;
while (runningThreads > 0)
{
runningThreads = 0;
foreach (Thread t in threads) if (t.ThreadState == ThreadState.Running) runningThreads++;
Thread.Sleep(100);
}
return errors.ToList<string>();
}
private void UrlWorker()
{
while (queue.Count > 0)
{
try
{
string url = "";
if (!queue.TryPop(out url)) continue;
if (TestFunc(url) != 200) errors.Push(url);
}
catch
{
break;
}
}
}
private int TestFunc(string url)
{
Thread.Sleep(new Random().Next(100));
return -1;
}
}
This class is designed to take a list of urls, scan them, then return a list of those which does not work. It uses multiple threads to avoid taking forever on long lists.
My problem is that even if i replace the actual scanning of urls with a test function which returns failure on all urls, the class returns a variable amount of failures.
I'm assuming my problem lies either with ConcurrentStack.TryPop() or .Push(), but I cant for the life of me figure out why. They are supposedly thread safe, and I've tried locking as well, no help there.
Anyone able to explain to me what I am doing wrong? I don't have a lot of experience with multiple threads..
public class UrlValidator
{
private const int MAX_THREADS = 10;
private List<Thread> threads = new List<Thread>();
private ConcurrentStack<string> errors = new ConcurrentStack<string>();
private ConcurrentStack<string> queue = new ConcurrentStack<string>();
public UrlValidator(List<string> urls)
{
queue.PushRange(urls.ToArray<string>());
}
public List<string> Start()
{
threads = new List<Thread>();
while (threads.Count < MAX_THREADS && queue.Count > 0)
{
var t = new Thread(new ThreadStart(UrlWorker));
threads.Add(t);
t.Start();
}
while (queue.Count > 0) Thread.Sleep(1000);
int runningThreads = 0;
while (runningThreads > 0)
{
runningThreads = 0;
foreach (Thread t in threads) if (t.ThreadState == ThreadState.Running) runningThreads++;
Thread.Sleep(100);
}
return errors.ToList<string>();
}
private void UrlWorker()
{
while (queue.Count > 0)
{
try
{
string url = "";
if (!queue.TryPop(out url)) continue;
if (TestFunc(url) != 200) errors.Push(url);
}
catch
{
break;
}
}
}
private int TestFunc(string url)
{
Thread.Sleep(new Random().Next(100));
return -1;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是任务并行库和PLINQ(并行 LINQ)非常擅长。查看一个示例,了解如果让 .NET 完成它的任务,事情将会变得多么容易:
只要有可能,您应该让 .NET 提供的库执行任何所需的线程管理。一般来说,TPL 非常适合此目的,但由于您只是转换单个项目集合,因此 PLINQ 非常适合此目的。您可以修改并行度(我建议将其设置为小于并发 TCP 连接的最大数量),并且可以像 LINQ 允许的那样添加多个条件。自动并行运行,并且使您无需进行线程管理。
This is something that the Task Parallel Library and PLINQ (Parallel LINQ) would be really good at. Check out an example of how much easier things will be if you let .NET do its thing:
Whenever possible, you should let the libraries .NET provides do any thread management needed. The TPL is great for this in general, but since you're simply transforming a single collection of items, PLINQ is well suited for this. You can modify the degree of parallelism (I would recommend setting it less than your maximum number of concurrent TCP connections), and you can add multiple conditions just like LINQ allows. Automatically runs parallel, and makes you do no thread management.
您的问题与 ConcurrentStack 无关,而是与您检查正在运行的线程的循环有关:
条件立即为 false,因此您实际上从未等待线程。反过来,这意味着
errors
将包含迄今为止运行的任何线程的错误。但是,您的代码还有其他问题,但手动创建线程可能是最大的问题。由于您使用的是 .NET 4.0,因此应该使用任务或 PLINQ 进行异步处理。使用 PLINQ,您的验证可以实现为:
Your problem has nothing to do with
ConcurrentStack
, but rather with the loop where you are checking for running threads:The condition is immediately false, so you never actually wait for threads. In turn, this means that
errors
will contain errors from whichever threads have run so far.However, your code has other issues, but creating threads manually is probably the greatest one. Since you are using .NET 4.0, you should use tasks or PLINQ for asynchronous processing. Using PLINQ, your validation can be implemented as: