【发布时间】:2011-12-10 14:49:08
【问题描述】:
我在需要执行一些 CPU 密集型工作的 C# 应用程序中使用线程池。顺便说一句,它似乎太慢了(编辑:它每 10 秒只打印几次调试字符串"Calculating on "
+ lSubArea.X + ":" + lSubArea.Y + " "
+ lSubArea.Width + ":" + lSubArea.Height,而我希望每几秒至少看到 NUM_ROWS_GRID^2 = 16 次),还通过更改 MinThreads SetMinThreads 方法。我不知道是否切换到自定义线程或者是否有办法加快它。在 Google 上搜索会返回一些结果,但没有任何效果;与 MSDN 的情况相同。
旧代码如下:
private void StreamerRoutine()
{
if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
while (this._state.WorkEnd == false)
{
// Ends time slice if video is off
if (this._state.VideoOn == false)
Thread.Sleep(0);
else
{
lock(this._state.AreaSync)
{
Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));
}
}
}
}
private void CreateDiffFrame(Object pState)
{
Rectangle lSubArea = (Rectangle)pState;
SmartDebug.DWL("Calculating on "
+ lSubArea.X + ":" + lSubArea.Y + " "
+ lSubArea.Width + ":" + lSubArea.Height);
// TODO : calculate frame
Thread.Sleep(0);
}
编辑:CreateDiffFrame 函数只是我曾经知道每秒调用多少次的存根。它将被 CPU 密集型工作取代,因为我定义了在这种情况下使用线程的最佳方式。
编辑:我删除了所有 Thread.Sleep(0);我认为这可能是一种加快例行程序的方法,但似乎它可能是一个瓶颈.. 新代码如下:
编辑:我将 WorkEnd 和 VideoOn 设置为 volatile 以避免缓存值和无限循环;我还添加了一个信号量,以使每一组工作项在前一组完成后开始。现在它工作得很好
private void StreamerRoutine()
{
if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
this._state.StreamingSem = new Semaphore(Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID, Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID);
while (this._state.WorkEnd == false)
{
if (this._state.VideoOn == true)
{
for (int i = 0; i < Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID; i++)
this._state.StreamingSem.WaitOne();
lock(this._state.AreaSync)
{
Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));
}
}
}
}
private void CreateDiffFrame(Object pState)
{
Rectangle lSubArea = (Rectangle)pState;
SmartDebug.DWL("Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height);
// TODO : calculate frame
this._state.StreamingSem.Release(1);
}
【问题讨论】:
-
为什么大家都在睡觉?
-
为什么大家都在睡觉?这是阻塞线程池线程而不是允许它们用于不同的作业。这将是非常低效的,并且不是线程池的预期使用方式。当您的示例代码在池中几乎没有要执行的工作时,我看不出您如何声称线程池很慢。
-
它可以在前一堆完成之前对项目进行排队。然后
this._state.WorkEnd和this._state.VideoOn在循环中永远不会改变,这也可能很危险,但可能一些隐式内存屏障可以让你避免成为一个实际的错误。 -
设置布尔值是原子的,但相关的内存屏障太弱了。但是原子性是不够的,读取代码可能会在一个陈旧的缓存值上工作,除非你之间有内存屏障。但你很幸运,你的锁和睡眠提供了这些障碍。如果不添加至少一个注释来记录这些障碍在哪里,以及哪些代码依赖于它们,我就不会依赖这些隐式内存障碍。 |如果新批次到货时您唯一的项目仍在工作,它们可能会在新批次上工作,因为您没有传递对不可变图像数据的引用。
-
如果
WorkEnd和VideoOn不是易失性,您编辑的代码可能会死锁。由于一旦它们都为假,计算机可以决定它们都将永远保持false并进入无限循环。
标签: c# multithreading performance threadpool queueuserworkitem