C# 为什么我的Thread2在加载图像时会减慢Thread1的速度?
希望你能帮助我:) 背景: 我正在用C#创建一个相对基本的图像查看器应用程序,其中包含一些特定功能 它的一个目标是以足够快的速度加载相对较大的图像(~8mb,仅.jpg),以提供一种免费的浏览体验(使用左右箭头键浏览图像) 我的解决方案: 为了实现这一点,我创建了一个带有循环缓冲区的BufferManager类。具有方法MaintainBuffer的类,该方法是一个无限循环,用于保持缓冲区已满。 它只是检查当前查看图像的左右两侧的缓冲区中是否有最少的K个图像,如果没有,则将图像加载到缓冲区中,直到有。 现在我自然地在一个新线程上调用这个MaintainBuffer方法,这样用户就可以在不注意BufferManager在后台工作的情况下浏览图像,应用程序就可以简单地向用户显示来自缓冲区的图像 我的解决方案的问题: 我的问题是,Thread2将图像加载到缓冲区会减慢Thread1(主线程)的速度,因此每次用户切换到新图像时,在将新图像加载到缓冲区时都会出现一个小的延迟峰值 我的调试尝试: 我检查了我的任务管理器,以确保线程不是在同一个内核上执行的,或者类似的东西,但是我可以看到在两个不同的内核上使用的两个明显的峰值。 这两个线程共享一些内存,其中包括缓冲区,因此我认为这可能就是问题所在,并尝试使用临时变量,只将引用移动到缓冲区中,但这没有帮助(可能是优化?) 现在我读到一些模糊的暗示,IO操作会减慢我所有的线程,但是这对我来说没有太大意义,因为CPU不应该真正参与将文件移动到主内存中,只有Thread2应该等待IO操作完成 我的问题:C# 为什么我的Thread2在加载图像时会减慢Thread1的速度?,c#,multithreading,image,io,C#,Multithreading,Image,Io,希望你能帮助我:) 背景: 我正在用C#创建一个相对基本的图像查看器应用程序,其中包含一些特定功能 它的一个目标是以足够快的速度加载相对较大的图像(~8mb,仅.jpg),以提供一种免费的浏览体验(使用左右箭头键浏览图像) 我的解决方案: 为了实现这一点,我创建了一个带有循环缓冲区的BufferManager类。具有方法MaintainBuffer的类,该方法是一个无限循环,用于保持缓冲区已满。 它只是检查当前查看图像的左右两侧的缓冲区中是否有最少的K个图像,如果没有,则将图像加载到缓冲区中,直
- 我是否错误地认为只有Thread2应该通过IO操作来减慢速度李>
- 问题可能是线程之间的数据共享吗?如果是,我该如何解决
- 还有什么会给我带来麻烦?我对多线程的理解是否遗漏了一些基本的东西
- 还有比我想做的更好的解决方案吗
private void browser_Load(object sender, EventArgs e)
{
//This is just getting the image and other file filenames (others needed for a feature)
string[] files = Directory.GetFiles(path);
imageSets = new Dictionary<string, List<string>>();
List<string> associatedFiles;
foreach (string f in files)
{
string filename = System.IO.Path.GetFileNameWithoutExtension(f);
string filenameExt = System.IO.Path.GetFileName(f);
if (imageSets.ContainsKey(filename))
{
imageSets.TryGetValue(filename, out associatedFiles);
associatedFiles.Add(filenameExt);
}
else
{
imageSets.Add(filename, new List<string>(){filenameExt});
}
}
//Getting only the names of the images
images = imageSets.Keys.ToList<string>();
//Creating a new bufferManager, note that the 'images' list is used by the object for getting the names of the images to load
bufferManager = new BufferManager(ref images, path);
//Have the bufferManager load the first image and put it in the buffer
currentImage = bufferManager.Init();
//Create a thread for maintaining the buffer
bufferThread = new Thread(bufferManager.MaintainBuffer);
bufferThread.Start();
//Display the intial image to the user
img_preview.BackgroundImage = currentImage;
}
这里的interesting方法是:MaintainBuffer,它在Thread2上调用
public void MaintainBuffer()
{
if (images.Count <= totalBufferSize)
{
//If the buffer can hold all the images without swapping images ind out, just load them all and dont worry about maintaining the buffer
totalBufferSize = images.Count;
for (int i = 0; i < totalBufferSize; ++i)
{
imageBuffer[i] = ImageTool.Resize(ImageTool.FromFile(path + "\\" + images[i] + ".jpg"), 460);
}
return;
}
while (!killBuffer)
{
//Ensure that enough images are buffered to the left of the current image. This looks a bit advanced due to being a circulair buffer, but the idea is simple
while (currentBufferIndex >= firstBufferIndex ? (currentBufferIndex - firstBufferIndex) < bufferWidth : (currentBufferIndex + totalBufferSize - firstBufferIndex) < bufferWidth)
{
--firstBufferIndex;
if (firstBufferIndex < 0)
firstBufferIndex = totalBufferSize - 1;
if (imageBuffer[firstBufferIndex] != null)
imageBuffer[firstBufferIndex].Dispose(); //Release the memory used by the image that is now "overwritten"
//Getting the image index of the image to load next
int imageNameIndex = currentIndex - (currentBufferIndex >= firstBufferIndex ? currentBufferIndex - firstBufferIndex : currentBufferIndex + totalBufferSize - firstBufferIndex);
if (imageNameIndex < 0)
imageNameIndex = images.Count + imageNameIndex;
Image fullSize = ImageTool.FromFile(path + "\\" + images[imageNameIndex] + ".jpg");
imageBuffer[firstBufferIndex] = ImageTool.Resize(fullSize, 460);
fullSize.Dispose();
}
//Ensure that enough images are buffered to the right of the current image
while (currentBufferIndex <= lastBufferIndex ? (lastBufferIndex - currentBufferIndex) < bufferWidth : (lastBufferIndex + totalBufferSize - currentBufferIndex) < bufferWidth)
{
++lastBufferIndex;
if (lastBufferIndex > totalBufferSize - 1)
lastBufferIndex = 0;
if (imageBuffer[lastBufferIndex] != null)
imageBuffer[lastBufferIndex].Dispose();
int imageNameIndex = (currentIndex + (currentBufferIndex <= lastBufferIndex ? lastBufferIndex - currentBufferIndex : lastBufferIndex + totalBufferSize - currentBufferIndex)) % (images.Count);
Image fullSize = ImageTool.FromFile(path + "\\" + images[imageNameIndex] + ".jpg");
imageBuffer[lastBufferIndex] = ImageTool.Resize(fullSize, 460);
fullSize.Dispose();
}
Thread.Sleep(100);
}
}
public-void-MaintainBuffer()
{
如果(images.Count=firstBufferIndex?(currentBufferIndex-firstBufferIndex)=firstBufferIndex?currentBufferIndex-firstBufferIndex:currentBufferIndex+totalBufferSize-firstBufferIndex);
如果(imageNameIndex<0)
imageNameIndex=images.Count+imageNameIndex;
Image fullSize=ImageTool.FromFile(路径+“\\”+图像[imageNameIndex]+.jpg”);
imageBuffer[firstBufferIndex]=ImageTool.Resize(fullSize,460);
fullSize.Dispose();
}
//确保在当前图像的右侧缓冲了足够的图像
while(currentBufferIndex totalBufferSize-1)
lastBufferIndex=0;
if(imageBuffer[lastBufferIndex]!=null)
imageBuffer[lastBufferIndex].Dispose();
int imageNameIndex=(currentIndex+(currentBufferIndex我很想知道为什么您认为CPU不参与内存的移动?对于初学者,您应该开始研究Interlocked
类来更改其中一些整数的值。您已经提到它们是从任一线程编辑的。。但是我看不到lock
或任何东西的用途。另外,Task Parallel Library为您提供了一些复杂线程解决方案的简单语法,您也可以研究一下。为什么要使用Thread.Sleep(100)为什么不使用ManualResetEvent
在需要重复时发出信号?@Simon我将查看Interlocked和该库。关于锁定,我只是在等待,看看在到达该部分之前是否获得了一些竞争条件。但是你认为库或Interlocked类可以帮助我解决性能问题吗?----CodeCamper我以前没有听说过这个术语,但是睡眠似乎是一个比使用基于事件的解决方案更简单的解决方案,它可以防止线程忙着等待太多。@JacobJensen:由于缺乏适当的同步,您的代码基本上无法分析。也许一个线程正在减慢另一个线程的速度,因为它们互相践踏。Like@codecamp建议,首先要消除延迟增加、CPU浪费的Sleep()循环,并使用适当的线程间信号。
public void MaintainBuffer()
{
if (images.Count <= totalBufferSize)
{
//If the buffer can hold all the images without swapping images ind out, just load them all and dont worry about maintaining the buffer
totalBufferSize = images.Count;
for (int i = 0; i < totalBufferSize; ++i)
{
imageBuffer[i] = ImageTool.Resize(ImageTool.FromFile(path + "\\" + images[i] + ".jpg"), 460);
}
return;
}
while (!killBuffer)
{
//Ensure that enough images are buffered to the left of the current image. This looks a bit advanced due to being a circulair buffer, but the idea is simple
while (currentBufferIndex >= firstBufferIndex ? (currentBufferIndex - firstBufferIndex) < bufferWidth : (currentBufferIndex + totalBufferSize - firstBufferIndex) < bufferWidth)
{
--firstBufferIndex;
if (firstBufferIndex < 0)
firstBufferIndex = totalBufferSize - 1;
if (imageBuffer[firstBufferIndex] != null)
imageBuffer[firstBufferIndex].Dispose(); //Release the memory used by the image that is now "overwritten"
//Getting the image index of the image to load next
int imageNameIndex = currentIndex - (currentBufferIndex >= firstBufferIndex ? currentBufferIndex - firstBufferIndex : currentBufferIndex + totalBufferSize - firstBufferIndex);
if (imageNameIndex < 0)
imageNameIndex = images.Count + imageNameIndex;
Image fullSize = ImageTool.FromFile(path + "\\" + images[imageNameIndex] + ".jpg");
imageBuffer[firstBufferIndex] = ImageTool.Resize(fullSize, 460);
fullSize.Dispose();
}
//Ensure that enough images are buffered to the right of the current image
while (currentBufferIndex <= lastBufferIndex ? (lastBufferIndex - currentBufferIndex) < bufferWidth : (lastBufferIndex + totalBufferSize - currentBufferIndex) < bufferWidth)
{
++lastBufferIndex;
if (lastBufferIndex > totalBufferSize - 1)
lastBufferIndex = 0;
if (imageBuffer[lastBufferIndex] != null)
imageBuffer[lastBufferIndex].Dispose();
int imageNameIndex = (currentIndex + (currentBufferIndex <= lastBufferIndex ? lastBufferIndex - currentBufferIndex : lastBufferIndex + totalBufferSize - currentBufferIndex)) % (images.Count);
Image fullSize = ImageTool.FromFile(path + "\\" + images[imageNameIndex] + ".jpg");
imageBuffer[lastBufferIndex] = ImageTool.Resize(fullSize, 460);
fullSize.Dispose();
}
Thread.Sleep(100);
}
}