Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

Lazy Alternatives - LazyAndWeak and BackgroundLoader

Rate me:
Please Sign up or sign in to vote.
4.96/5 (10 votes)
1 Dec 2011CPOL7 min read 26.3K   400   27   7
This article will present two alternatives to Lazy.

Introduction

This article will present two alternatives to Lazy. One of them is focused on low memory use and the other focused on responsiveness.

Background

.NET 4 added the Lazy class. Its purpose is to allow fast load times and reduce memory consumption.

But, should I say that I consider Lazy class... too lazy? There are two problems that I see when using the Lazy class:

  1. The application loads fast, but then you must wait for each item that you access for the first time.
  2. If you load an item to use it only once, it will be kept in memory forever (ok... until the lazy object itself is collected).

So, to solve those problems, I decided to create my own solution, which I will present here.

Lazy Alternatives

I really wanted to create a solution that is both responsive and that avoids using memory when it is not needed. But unfortunately that's impossible... at least for me and as a generic purpose solution. But I can solve one of those problems at a time.

So I did it, as two different classes:

  • LazyAndWeak - Better memory management
  • BackgroundLoader - Better responsiveness

LazyAndWeak

For human beings, being lazy is usually bad. Being weak too is even worse. But for programming languages, being lazy and weak is good, especially when we want to save memory.

LazyAndWeak only creates its Value when it is first requested, as happens with Lazy class. But it also stores the value as a weak-reference, so it can be collected if memory is needed. Ok, it is a bit more than that. To avoid values to be collected at each garbage collection, I use my GCUtils.KeepAlive method so recently used items are not collected in the next full collection, so constantly used items will always be in memory, even after a full collection.

The big downside here is that after the item is unloaded from memory, if it is needed again, the user will need to wait again and any value that was stored in such item will be lost. So, it is very important that the constructor/creator knows how to get the most up-to-date information.

BackgroundLoader

The BackgroundLoader class goes in the opposite direction of LazyAndWeak. Instead of creating items only when requested and then allowing them to die, it tries to load all items in the background, trying not to affect performance of the application and then keeps them there. So, you load the application in a very fast manner, similar to what happens to any Lazy alternative but then when a CPU is idle, it starts loading the items that you didn't use yet.

I say that it goes in the opposite direction because it will not save any memory, as sooner or later all the items will be loaded and kept in memory. But except from the very first item (in case the user requests it as soon the application is loaded), the user will not need to wait for items to load. The user opens the application (in a very fast manner), maybe waits for the very first operation to load its items but, later, any other operation will be done instantly as the items will be already loaded.

When To Use Each One?

In my opinion, never use Lazy. Maybe I am too drastic, but I don't see a real situation where Lazy only will be better. For example, if your application has 1000 menu items, each one with a slow load-time, and your users are probably only using 10 of those 1000 menu items, then it is OK.

But what happens when the user does load those 1000 items? They will be kept in memory forever. So, the user will need to wait 1000 times and then those will be there forever (so new requests will not wait).

But if those 1000 items consume too much memory, I'll say that you should use LazyAndWeak, after all the computer gets slower when running out-of-memory. If you think it is OK to keep all of them in memory, then use BackgroundLoader as the user will wait for the program to load, maybe will wait for the first menu item to load... and if they stay enough time there, when they use any other item it will be already in memory, without the need to wait again.

In fact, for very large projects, I usually open the application, go out for a while and then return, expecting everything to be loaded. I consider it frustrating that, in Visual Studio, I end-up waiting again when I try to load an ASP.NET or WPF project for the first time.

Implementation

I know CodeProject is for code... but should I really show the code here? I think explaining the basic idea is enough. So, I will only explain the idea.

LazyAndWeak uses a lock when creating the value (so two threads will not create duplicate values). When accessing the Value, a call to GCUtils.KeepAlive is made. I explained that class in one of my first articles, WeakReferences as a Good Caching Mechanism. The main problem of using WeakReferences only is that items are discarded at every collection, even if they were recently used. This class forbids them to be collected at the next collection but allows them to be collected if a second collection happens and they are still unused.

BackgroundLoader is harder. A "loader" thread is created. Each time a LazyLoader is created, it registers itself inside that thread and sets an event (ManagedAutoResetEvent) so the loader thread starts to run. If 1000 items are registered, they will be loaded in order (there is not the risk of 1000 threads trying to load their items). This class also uses locking when accessing its Value so if it is not loaded yet, it loads it immediately and removes this BackgroundLoader instance from the loader class, as it will be already loaded.

Also, there is the problem that you may create the background loader and then you discard it (in the sample project, when you change from one directory to another). That's why I implemented the IDisposable interface. If the item is not loaded yet, it is in the list of "to load" items. If you discard it, it simply removes such item from the "to load" list.

How to Use the Classes

Using those classes is very similar to using the .NET Lazy class. You simply create the LazyAndWeak<SomeType> or BackgroundLoader<SomeType> at the very first moment, probably with a declaration like this:

C#
private readonly LazyAndWeak<SomeType> field = new LazyAndWeak<SomeType>();

And when you need to access the SomeType instance, you must access the Value property of the created field.

It is important that SomeType has a default constructor or that you give a creator delegate to it, or you will get an exception. Also, as happens with lazy, those helper objects are only useful to hold slow loading or memory-hungry objects. Don't use them for fast and small objects, as the helper objects themselves consume memory.

Sample

The attached sample is a very simple image visualizer. Its purpose is only to show the differences of the various techniques.

When you enter any directory, all image names are read and some type of lazy-loading will be done. When just opening or scrolling large folders, you will see red lines instead of images. Red lines means such images are not still in memory.

If you force collection, already loaded images will be reloaded using LazyAndWeak (which shows it worked) but will not have any effects on BackgroundLoader or custom Lazy.

I forced a 100 milliseconds delay when loading images. That was on purpose to make the differences more visible as loading images is not that slow.

If you are using BackgroundLoader, when you change directory, it will stop loading the old files. That's why Dispose was needed. If I didn't do that, if I entered directory A, then B... B will only load after loading all (useless) A files.

History

  • 1st December, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions

 
QuestionHow topical is this today? Pin
Martin_Egli8-Aug-20 11:24
Martin_Egli8-Aug-20 11:24 
QuestionReactive Extension (Rx)? Pin
unknowndentified101111-Dec-11 20:36
unknowndentified101111-Dec-11 20:36 
AnswerRe: Reactive Extension (Rx)? Pin
Paulo Zemek2-Dec-11 0:16
mvaPaulo Zemek2-Dec-11 0:16 
SuggestionLazyAndWeakAndReactiveAndDependent (LWRD) Pin
AspDotNetDev1-Dec-11 9:02
protectorAspDotNetDev1-Dec-11 9:02 
GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD) Pin
Paulo Zemek1-Dec-11 9:41
mvaPaulo Zemek1-Dec-11 9:41 
GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD) Pin
AspDotNetDev1-Dec-11 10:22
protectorAspDotNetDev1-Dec-11 10:22 
GeneralRe: LazyAndWeakAndReactiveAndDependent (LWRD) Pin
Paulo Zemek1-Dec-11 10:49
mvaPaulo Zemek1-Dec-11 10:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.