我使用下面的代码来执行使用线程的任务,这里我尝试执行“dtTable”Datatable中的所有记录。有限的线程数为2(即时间只有两个线程/仅允许执行)。问题是它没有执行Datatable中可用的所有记录,它以不规则的方式执行数据。可能是什么问题..?提前致谢。
public class Generator : IDisposable
{
public static int maxThreadCount = 2;
public static int runningThreadCount = 0;
public RunTask()
{
for (int ro = 0; ro <= dtTable.Rows.Count - 1; ro++)
{
if (maxThreadCount > runningThreadCount)
{
Thread atpthread = new Thread(delegate()
{
DoOperationMethod(dtTable.Rows[ro], Task, startDate, EndDate, dtTemplate);
});
atpthread.Start();
runningThreadCount = runningThreadCount + 1;
Mainthreads.Add(atpthread);
}
else
{
ro--;
}
}
}
public void DoOperationMethod(DataRow drAttachpoint, System.StrTaskItem Task, DateTime startDate, DateTime EndDate, DataTable dtTemplate)
{
//doing my Operation
runningThreadCount = runningThreadCount-1; //Once Task done count will get reduce
}
}
我正在使用.net 3.5(仅供参考)。
答案 0 :(得分:4)
问题是你在等待线程变为空闲时继续迭代数据表中的行。在任何情况下,这种多线程,即使它实际上是正确的(它不是),也是非常低效的 - 大部分时间都可能用于启动新线程。
尝试这样的事情:
Parallel
.ForEach(dtTable.Rows.OfType<DataRow>(), row => DoOperationMethod(...))
.WithDegreeOfParallelism(2);
修改强>
要清除问题的来源,您必须了解如何在匿名方法中捕获变量。您的DoOperationMethod
调用未被传递到您想要的数据行,因为ro
“变量”未被复制,而是被引用。因此,当ro
在循环中发生变化时,它也会在您创建的主题中发生变化。
这是因为您的代码非常不安全且效率低下:
ro
,这几乎完全是纯粹的CPU工作。在等待结果时,这比简单地阻塞要多得多。runningThread
最终为2
的情况下使整个事件死锁,而没有线程正在运行。Mainthreads
是某种类型的列表,我假设您也从DoOperationMethod
方法修改它 - 再次,这会导致随机异常和意外结果。maxThreadCount > runningThreadCount
。实际上,在x86 CPU上的当前.NET上,这对于像这样复杂的方法来说不太可能,但是当你更新到.NET 7.0或其他任何东西时,它会让你感到害怕:)多线程 hard 。你真的不想猜测你的方式。至少,首先尝试理解基础知识 - http://www.albahari.com/threading/。
答案 1 :(得分:2)
在我看来,您的代码最大的问题是,即使您修复了线程不安全的runningThreadCount
用法,您的代码也在等待某个线程完成时“旋转”。当你试图完成真正的工作时,这完全占用了CPU核心。
Luann提出的解决方案很好,但我会使用Cast<DataRow>()
而不是OfType<DataRow>()
(因为枚举中的所有元素实际上都应该是DataRow
类型) 。除了简洁之外,一个很大的优点是它使用线程池,这将显着减少线程管理的开销(因为它重用线程而不是一遍又一遍地创建和销毁它们)。
如果您更喜欢更明确的方法,可以修改您发布的代码以使用信号量:
SemaphoreSlim semaphore = new SemaphoreSlim(2);
for (int ro = 0; ro <= dtTable.Rows.Count - 1; ro++)
{
semaphore.Wait();
DataRow row = dtTable.Rows[ro];
Thread atpthread = new Thread(delegate()
{
DoOperationMethod(row, Task, startDate, EndDate, dtTemplate);
semaphore.Release();
});
atpthread.Start();
Mainthreads.Add(atpthread);
}
这将导致主线程在semamphore计数达到0时阻塞Wait()
调用,并在计数再次为正时继续(即在线程调用Release()
之后)。
我注意到评论者关于dtTable
是否安全使用的观点。我在这里假设在此处理期间没有修改对象。有了这个假设,使用它不同步应该没问题。但如果这些假设是错误的,那么我同意他们认为这是代码中的另一个错误。
最后,我将指出您看到行被跳过的原因是您在匿名方法中使用变量ro
。在匿名方法为循环的给定迭代执行之前,该变量可以很容易地递增,从而导致该线程处理错误的行。某些行可能会被多次处理,而其他行则会被跳过。我通过检索循环块内变量中的DataRow
对象解决了上述代码示例中的问题,以便每个线程都获得自己的变量私有副本,其中包含DataRow
个对象它应该处理。