希望你能帮助我:)。
背景
我正在C#中创建一个具有一些特定功能的相对基本的图像查看器应用程序。
其中一个目标是以足够快的速度加载相对较大的图像(~8mb,真正只有.jpg),以提供无滞后的浏览体验(使用左右箭头键翻转图像)。
我的解决方案:
为了达到这个目的,我创建了一个带有circulair缓冲区的BufferManager类。具有MaintainBuffer方法的类,它是一个保持缓冲区填充的无限循环。 它只是检查当前查看图像的左侧和右侧缓冲区中是否存在最少的K个图像,如果没有,则将图像加载到缓冲区中直到存在。 现在我自然地在一个新线程上调用这个MaintainBuffer方法,这样用户可以翻转图像而不会注意到BufferManager在后台工作,因此应用程序可以简单地向用户显示缓冲区中的图像。
我的解决方案存在的问题:
我的问题是,Thread2将图像加载到缓冲区正在减慢我的Thread1(主线程),因此每次用户翻转到新图像时都会有一个小的lagspike,同时新的图像被加载到缓冲区。
我尝试调试:
我检查了我的任务管理器以确保线程没有在同一个核心上执行或类似的东西,但我可以看到在两个不同的核心上使用两个明显的峰值。 两个线程共享一些内存,其中包括缓冲区,所以我认为可能是问题并尝试使用临时变量并仅将引用移动到缓冲区,这没有帮助(优化可能?)。
现在我读到的东西模糊地暗示IO操作会减慢我的所有线程,但是这对我来说没有多大意义,因为CPU不应该真正涉及将文件移动到主内存中而只有Thread2应该等待IO操作完成。
我的问题:
代码:
在我的Form1类中调用线程:
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;
}
BufferManager的Init方法:
public Image Init()
{
currentIndex = lastBufferIndex = firstBufferIndex = 0;
Image fullSize = ImageTool.FromFile(path + "\\" + images[0] + ".jpg");
//Resize the image in the buffer to save memory and display faster
imageBuffer[0] = ImageTool.Resize(fullSize, 460); //The buffer is a simple array of fixed size
fullSize.Dispose();
return imageBuffer[0];
}
这里的interresting方法:在Thread2上调用的MaintainBuffer
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);
}
}
我省略了左右翻动图像的代码,因为它非常简单。然而,它确实更新了Thread1上的BufferManager对象的currentIndex(正在查看的图像的索引)和currentBufferIndex(circulair缓冲区中的当前图像位置),这是可能导致问题的数据共享示例?
我可能没有注意到多线程的经验,所以我希望你能给我一些指示!感谢