线程池在c#中太慢了,有没有办法加速它?Thread.Sleep(0)和QueueUserWorkItem问题
本文关键字:Sleep Thread 问题 QueueUserWorkItem 加速 有没有 线程 | 更新日期: 2023-09-27 18:17:39
我在一个需要做一些cpu密集型工作的c#应用程序中使用Threadpool。顺便说一下,它似乎太慢了(编辑:它每10秒只打印出调试字符串"Calculating on "
+ lSubArea.X + ":" + lSubArea.Y + " "
+ lSubArea.Width + ":" + lSubArea.Height
几次,而我期望看到至少NUM_ROWS_GRID^2 = 16次每隔几秒),也通过SetMinThreads
方法改变MinThreads。我不知道是否切换到自定义线程,或者是否有办法加快它。在谷歌搜索返回一些结果,但没有工作;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易失性,以避免缓存值和无限循环;我还添加了一个信号量,使每一堆工作项在前一堆完成后开始。现在它工作得很好
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);
}
从我所看到的情况来看,确实没有一个好的方法来告诉您究竟是什么使您的代码变慢,但是有一些事情很突出:
-
thread . sleep(0)。当你这样做的时候,你放弃了OS的剩余时间片,并减慢了一切,因为CreateDiffFrame()在OS调度器返回之前实际上不能返回。
-
Rectangle的对象强制转换,它是一个结构体。当这种情况发生时,您会产生装箱的开销,这不是您想要的真正的计算密集型操作。
-
你的锁调用(this._state.AreaSync)。这可能是AreaSync也被锁定在其他地方,这可能会减慢速度。
-
您可能将项排队的粒度太细了——如果您将非常小的工作项排队,那么与实际完成的工作量相比,每次将这些项放入队列的开销可能会很大。您还可以考虑将内部循环的内容放在排队的工作项中,以减少此开销。
如果这是你试图为并行计算做的事情,你是否研究过使用PLINQ或其他类似的框架?
我猜这是在CreateDiffFrame结束时的Sleep。如果我没记错的话,这意味着每个线程至少还能存活10毫秒。您可以在不到10毫秒的时间内完成实际的工作。ThreadPool试图优化线程的使用,但我认为它有一个未完成线程总数的上限。因此,如果您想实际模拟您的工作负载,请创建一个紧循环,直到预期的毫秒数过去,而不是Sleep。
无论如何,我不认为使用ThreadPool是真正的瓶颈,使用其他线程机制不会加快你的代码。
在KB976898中描述的ThreadPool.SetMinThreads
方法存在已知错误:
使用ThreadPool后。.NET Framework 3.5中的SetMinThreads方法,由线程池维护的线程不能按预期工作
你可以从这里下载修复这个行为