Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我的Thread2在加载图像时会减慢Thread1的速度?_C#_Multithreading_Image_Io - Fatal编程技术网

C# 为什么我的Thread2在加载图像时会减慢Thread1的速度?

C# 为什么我的Thread2在加载图像时会减慢Thread1的速度?,c#,multithreading,image,io,C#,Multithreading,Image,Io,希望你能帮助我:) 背景: 我正在用C#创建一个相对基本的图像查看器应用程序,其中包含一些特定功能 它的一个目标是以足够快的速度加载相对较大的图像(~8mb,仅.jpg),以提供一种免费的浏览体验(使用左右箭头键浏览图像) 我的解决方案: 为了实现这一点,我创建了一个带有循环缓冲区的BufferManager类。具有方法MaintainBuffer的类,该方法是一个无限循环,用于保持缓冲区已满。 它只是检查当前查看图像的左右两侧的缓冲区中是否有最少的K个图像,如果没有,则将图像加载到缓冲区中,直

希望你能帮助我:)

背景:

我正在用C#创建一个相对基本的图像查看器应用程序,其中包含一些特定功能

它的一个目标是以足够快的速度加载相对较大的图像(~8mb,仅.jpg),以提供一种免费的浏览体验(使用左右箭头键浏览图像)

我的解决方案:

为了实现这一点,我创建了一个带有循环缓冲区的BufferManager类。具有方法MaintainBuffer的类,该方法是一个无限循环,用于保持缓冲区已满。 它只是检查当前查看图像的左右两侧的缓冲区中是否有最少的K个图像,如果没有,则将图像加载到缓冲区中,直到有。 现在我自然地在一个新线程上调用这个MaintainBuffer方法,这样用户就可以在不注意BufferManager在后台工作的情况下浏览图像,应用程序就可以简单地向用户显示来自缓冲区的图像

我的解决方案的问题:

我的问题是,Thread2将图像加载到缓冲区会减慢Thread1(主线程)的速度,因此每次用户切换到新图像时,在将新图像加载到缓冲区时都会出现一个小的延迟峰值

我的调试尝试:

我检查了我的任务管理器,以确保线程不是在同一个内核上执行的,或者类似的东西,但是我可以看到在两个不同的内核上使用的两个明显的峰值。 这两个线程共享一些内存,其中包括缓冲区,因此我认为这可能就是问题所在,并尝试使用临时变量,只将引用移动到缓冲区中,但这没有帮助(可能是优化?)

现在我读到一些模糊的暗示,IO操作会减慢我所有的线程,但是这对我来说没有太大意义,因为CPU不应该真正参与将文件移动到主内存中,只有Thread2应该等待IO操作完成

我的问题:

  • 我是否错误地认为只有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;
    }
这里的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);
        }

    }