Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/hadoop/6.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#-如何使用线程实现图像预加载缓存_C#_Multithreading - Fatal编程技术网

C#-如何使用线程实现图像预加载缓存

C#-如何使用线程实现图像预加载缓存,c#,multithreading,C#,Multithreading,在我的应用程序中,有一个图像列表,用户可以在其中单步执行。图像加载速度较慢,因此为了改善用户体验,我想在后台预加载一些图像(例如,列表中当前选定图像的后续图像) 我从来没有在C#中真正使用过线程,因此我正在寻找一些关于如何实现以下行为的“最佳实践”建议: public Image LoadCachedImage(string path) { // check if the cache (being operated in the background) // has prelo

在我的应用程序中,有一个图像列表,用户可以在其中单步执行。图像加载速度较慢,因此为了改善用户体验,我想在后台预加载一些图像(例如,列表中当前选定图像的后续图像)

我从来没有在C#中真正使用过线程,因此我正在寻找一些关于如何实现以下行为的“最佳实践”建议:

public Image LoadCachedImage(string path) 
{
    // check if the cache (being operated in the background)
    // has preloaded the image
    Image result = TryGetFromCache(path);

    if (result == null) { result = LoadSynchronously(path); }

    // somehow get a list of images that should be preloaded,
    // e.g. the successors in the list
    string[] candidates = GetCandidates(path);

    // trigger loading of "candidates" in the background, so they will
    // be in the cache when queried later
    EnqueueForPreloading(candidates);

    return result;   
}

我认为,后台线程应该监视队列,并连续处理通过enqueueforpreload()发布的元素。我想知道如何实现后台工作线程的“主循环”(或者有更好的方法吗?

如果确实需要对候选线程进行顺序处理,可以执行以下操作之一:

  • 创建具有AutoResetEvent的消息队列数据结构。该类应该生成一个线程,该线程等待事件,然后处理队列中的所有内容。类的Add或Enqueue应该将其添加到队列中,然后设置事件。这将释放处理队列中项目的线程

  • 创建一个类,该类启动STA线程,创建System.Windows.Forms.Control,然后进入Application.Run()。每次要异步处理映像时,调用Control.BeginInvoke(…),STA线程将在其消息队列中拾取它

  • 可能还有其他的选择,但这两个将是我要尝试的

    如果您不需要顺序处理,请考虑使用THEADPOLL。QueueUserWorkItem(…)。如果有空闲池线程,它将使用它们,否则它将对项目进行排队。但是你不能保证处理的顺序,有几个可能/将同时得到处理

    下面是一个(有缺陷的)消息队列示例:

    class MyBackgroundQueue<T>
    {
        private Queue<T> _queue = new Queue<T>();
        private System.Threading.AutoResetEvent _event = new System.Threading.AutoResetEvent(false);
        private System.Threading.Thread _thread;
    
        public void Start()
        {
            _thread = new System.Threading.Thread(new System.Threading.ThreadStart(ProcessQueueWorker));
            _thread.Start();
        }
    
        public class ItemEventArgs : EventArgs
        { public T Item { get; set; } }
    
        public event EventHandler<ItemEventArgs> ProcessItem;
    
        private void ProcessQueueWorker()
        {
            while (true)
            {
                _event.WaitOne();
                while (_queue.Count > 0)
                    ProcessItem(this, new ItemEventArgs { Item = _queue.Dequeue() });
            }
        }
    
        public void Enqueue(T item)
        {
            _queue.Enqueue(item);
            _event.Set();
        }
    }
    
    类MyBackgroundQueue
    {
    专用队列_Queue=新队列();
    private System.Threading.AutoResetEvent _event=new System.Threading.AutoResetEvent(false);
    私有系统.Threading.Thread\u线程;
    公开作废开始()
    {
    _thread=new System.Threading.thread(new System.Threading.ThreadStart(ProcessQueueWorker));
    _thread.Start();
    }
    公共类ItemEventArgs:EventArgs
    {public T Item{get;set;}}
    公共事件事件处理程序ProcessItem;
    私有void ProcessQueueWorker()
    {
    while(true)
    {
    _event.WaitOne();
    而(_queue.Count>0)
    ProcessItem(这是新的ItemEventArgs{Item=_queue.Dequeue()});
    }
    }
    公共无效排队(T项)
    {
    _排队。排队(项目);
    _event.Set();
    }
    }
    

    当然,这里的一个缺陷是_队列未锁定,因此您将遇到竞争条件。但我会让您来解决这个问题(例如,使用2队列交换方法)。此外,while(true)永远不会中断,但我希望该示例符合您的目的。

    如果您确实需要对候选对象进行顺序处理,您可以执行以下操作之一:

  • 创建具有AutoResetEvent的消息队列数据结构。该类应该生成一个线程,该线程等待事件,然后处理队列中的所有内容。类的Add或Enqueue应该将其添加到队列中,然后设置事件。这将释放处理队列中项目的线程

  • 创建一个类,该类启动STA线程,创建System.Windows.Forms.Control,然后进入Application.Run()。每次要异步处理映像时,调用Control.BeginInvoke(…),STA线程将在其消息队列中拾取它

  • 可能还有其他的选择,但这两个将是我要尝试的

    如果您不需要顺序处理,请考虑使用THEADPOLL。QueueUserWorkItem(…)。如果有空闲池线程,它将使用它们,否则它将对项目进行排队。但是你不能保证处理的顺序,有几个可能/将同时得到处理

    下面是一个(有缺陷的)消息队列示例:

    class MyBackgroundQueue<T>
    {
        private Queue<T> _queue = new Queue<T>();
        private System.Threading.AutoResetEvent _event = new System.Threading.AutoResetEvent(false);
        private System.Threading.Thread _thread;
    
        public void Start()
        {
            _thread = new System.Threading.Thread(new System.Threading.ThreadStart(ProcessQueueWorker));
            _thread.Start();
        }
    
        public class ItemEventArgs : EventArgs
        { public T Item { get; set; } }
    
        public event EventHandler<ItemEventArgs> ProcessItem;
    
        private void ProcessQueueWorker()
        {
            while (true)
            {
                _event.WaitOne();
                while (_queue.Count > 0)
                    ProcessItem(this, new ItemEventArgs { Item = _queue.Dequeue() });
            }
        }
    
        public void Enqueue(T item)
        {
            _queue.Enqueue(item);
            _event.Set();
        }
    }
    
    类MyBackgroundQueue
    {
    专用队列_Queue=新队列();
    private System.Threading.AutoResetEvent _event=new System.Threading.AutoResetEvent(false);
    私有系统.Threading.Thread\u线程;
    公开作废开始()
    {
    _thread=new System.Threading.thread(new System.Threading.ThreadStart(ProcessQueueWorker));
    _thread.Start();
    }
    公共类ItemEventArgs:EventArgs
    {public T Item{get;set;}}
    公共事件事件处理程序ProcessItem;
    私有void ProcessQueueWorker()
    {
    while(true)
    {
    _event.WaitOne();
    而(_queue.Count>0)
    ProcessItem(这是新的ItemEventArgs{Item=_queue.Dequeue()});
    }
    }
    公共无效排队(T项)
    {
    _排队。排队(项目);
    _event.Set();
    }
    }
    

    当然,这里的一个缺陷是_队列未锁定,因此您将遇到竞争条件。但我会让您来解决这个问题(例如,使用2队列交换方法)。另外,while(true)从不中断,但我希望该示例能够满足您的需要。

    这就是我所说的作弊缓存。操作系统已经为您缓存文件,但您必须先访问它们。因此,您可以只加载文件,而不保存对它们的引用

    您可以在不使用多线程本身的情况下执行此操作,也不必将图像保存在列表中。只需为要在后台加载的每个文件创建一个方法委托和调用

    例如,预加载目录中的所有jpeg图像

    Action<string> d = (string file) => { System.Drawing.Image.FromFile(file); };
    foreach(string file in dir.GetFiles("*.jpg"))
        d.BeginInvoke(file);
    
    Action d=(字符串文件)=>{System.Drawing.Image.FromFile(文件);};
    foreach(dir.GetFiles(“*.jpg”)中的字符串文件)
    d、 BeginInvoke(文件);
    
    BeginInvoke()是一种多线程方法,循环速度非常快,但每个文件将加载到不同的线程上。或者你可以改变
    Action<List<string>> d = PreCache;
    d.BeginInvoke(theList);