The reminder of the dangers of premature optimization are in the comments, so I will address the semantics of what's going on here.
Like the article points out, the ConcurrentQueue
can hold on to references of some things that have already gone through it. I learned it as 'a few dozen' and the article says it is no more than 31, which seems to gel pretty nicely. If the queue is tracking big objects, like your 2000x2000 Bitmaps, that can theoretically become a problem. It depends on what the rest of your program is doing, of course.
Wrapping it in a StrongBox<T>
helps because the only thing StrongBox
does is hold onto a reference to something else. Therefore, a StrongBox
has a very tiny footprint, and whatever it holds will go out of scope and (theoretically) get GC'd quicker.
Since StrongBox
has all the content of diet soda, you're kind of overthinking its usage. You literally just load up the Value
field with some T
and then reference it later. It looks a little like this:
var boxedBitmap = new StrongBox<Bitmap>(new Bitmap(1,1));
var bitmap = boxedBitmap.Value;
Or alternatively:
var boxedBitmap = new StrongBox<Bitmap>();
boxedBitmap.Value = new Bitmap(1,1);
var bitmap = boxedBitmap.Value;
Seriously, the implementation of this class if you pop it open in Reflector is like 5 lines.
This being the case, your usage of ConcurrentQueue<T>
is not really any different from the usage of ConcurrentQueue<StrongBox<T>>
. You'll simply tack on .Value
before you send the resource to its destination thread. This did help a company I worked for reduce the memory imprint of a massive multithreaded analysis service by quite a bit by simply passing around a reference to a deterministic tool instead of passing the entire tool around, but your mileage may vary - I am not clear on what ramifications it would have if you were passing something to be mutated and then used by something else.