Usage of ConcurrentQueue<StrongBox<T>>
Asked Answered
B

1

11

I am basically looking for a container of image collections acquired from camera in a thread. Since ConcurrentQueue is thread-safe, I wanted to use it. But while debugging my code, I found this article saying

If the elements are small, you’ll probably never notice this. If, however, the elements hold on to large resources (e.g. each element is a huge image bitmap), it’s possible you could see the impact of this (one workaround is to queue a wrapper object, e.g. have a ConcurrentQueue<StrongBox<T>> rather than a ConcurrentQueue<T>, and null out the wrapper’s reference to the T value after the wrapper has been dequeued).

As far as I can see, StrongBox is a kind of wrapper for original value. Does that mean I have to store another collection of the images?

So I am looking for an usage or an example of ConcurrentQueue<StrongBox<T>>. Only thing I found from google is this code.

Byelostok answered 28/11, 2012 at 18:37 Comment(5)
Are you currently noticing problems when profiling your code that indicate references to these images are being held onto for significantly longer than they should be? While it's certainly possible that the issue described in the article is affecting you, it's not likely that it's a problem. You need to use the queue in a very specific way to cause problems (i.e. leave lots of open iterators for long periods of time, have both very large elements and a very slow rate of dequeuing, don't have any other references to the big items, etc.Garrity
@Garrity Thanks for your comment. I didn't do profile my code yet but image processing in that part is slower than I expected. I don't think my collection will be that many (at most 10-20 hopefully) but each image is a large one (2048x2048) So profiling will be my next work then. :-)Byelostok
This issue, if you have one, wouldn't have any effect on the speed of the program. It would potentially mean your program is using some memory for longer than it needs to.Garrity
StrongBox is an obscure way to turn possible large value type values into a reference. It is used by System.Linq.Expressions, a namespace that can't make assumptions about data. The odds that it will be helpful for images ought to be zero.Benzaldehyde
That same article mentions that "For .NET 4.5, we’ve changed the design to strike what we believe to be a good balance. Dequeued elements are now nulled out as they’re dequeued, unless there’s a concurrent enumeration happening" so you can also just upgrade to the latest framework and not even worry about this, even though it's likely not an issue at all.Kathrinekathryn
S
9

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.

Swinge answered 28/11, 2012 at 21:58 Comment(5)
I'm probably just tired, but if a StrongBox holds a reference to the object, and the queue holds onto up to 31 StrongBoxes, how will the GC be able to get to anything sooner? (What am I missing?)Wyant
I think the point is that you can null the contents of a StrongBox, but you can't get at the ConcurrentQueue internals to null the position in the queue you were just using. Therefore the GC can collect the big object, even though the queue is still holding onto the tiny StrongBox.Chef
I came here to grep StrongBox and this article plus commentary was a huge help, thanks! In practice though I would probably just write my own StrongBox like thing though, for various reasons.Trista
It's also worth mentioning that the implementation in .NET 4.5 was changed to fully remove each object on dequeue, unless an active enumerator is running somewhere (in which case it must leave them for the enumeration to work). So, for regular enqueue/dequeue usage, no references will be held after dequeuing.Aceydeucy
Very minor correction/quibble to your excellent explanation; you cite the article's mention of "[holding] on to the last <= 31 dequeued elements," but this worst-case analysis only applies to normal non-enumeration operation. As is required by "snapshot" semantics, each/any issued/pending enumerator reference will obviously keep alive all the elements it has yet to report--an unbounded number--even if the entire queue is subsequently cleared (and indefinitely thereafter). Anyway, given what "snapshot" so obviously entails, your "...no more than 31..." is just a wording technicality really.Intersperse

© 2022 - 2024 — McMap. All rights reserved.