我每天都有报道我试图从远程网络服务中集体收集我的代码如下:
public static void ProcessEnMasse(System.DateTime fromDate, DateTime endDate)
{
System.Threading.ThreadPool.SetMaxThreads(10, 10);
for (System.DateTime d = fromDate; d <= endDate; d = d.AddDays(1))
{
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(day => ProcessOneDay(d)));
}
}
public static void ProcessOneDay(System.DateTime theDate)
{
Log.Debug(string.Format("Processing {0:yyyy-MM-dd}...", theDate));
var thePackager = new DataPackager();
thePackager.CreateDatabaseImportPackage(theDate, theDate, true, false);
}
...当我查看日志时,我注意到有几个线程正在处理相同的日期。为什么会这样,我需要做些什么才能防止这种情况发生?
答案 0 :(得分:3)
你需要注意闭包(我会尽快给出更全面的解释)
基本上,您的代码应为:
public static void ProcessEnMasse(System.DateTime fromDate, DateTime endDate)
{
System.Threading.ThreadPool.SetMaxThreads(10, 10);
for (System.DateTime d = fromDate; d <= endDate; d = d.AddDays(1))
{
System.DateTime newD = d;
System.Threading.ThreadPool.QueueUserWorkItem
(new System.Threading.WaitCallback(day => ProcessOneDay(newD)));
}
}
Here is Jon Skeet's chapter on closures
高级别的想法是(在原始代码中)传入d
时,它实际上捕获了该变量,编译后的代码将使该变量充当全局共享变量。所以,当您点击下一个for
步骤时,d
不仅会更新您的循环,而且会更新您刚刚传入的函数。
这是高级别的,但我真的主张阅读Jon Skeet的文章,因为它写得非常好。
Here is another article如果您正在寻找更多:)
答案 1 :(得分:2)
所有lambda表达式共享相同的d
变量。
如果其中一个工作项仅在初始线程运行d = d.AddDays(1)
后启动,则它将使用该日期。
要解决此问题,请在循环体内声明一个单独的局部变量,然后使用它。
答案 2 :(得分:2)
你是“capturing the loop variable”。
要解决它:
for (System.DateTime d = fromDate; d <= endDate; d = d.AddDays(1))
{
DateTime copy = d;
System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(day => ProcessOneDay(copy)));
}
或者您可以这样写(注意上面的代码中没有使用day
):
for (System.DateTime d = fromDate; d <= endDate; d = d.AddDays(1))
{
ThreadPool.QueueUserWorkItem(day => ProcessOneDay((DateTime) day), d);
}