Reputation: 5414
I have a ushort counter (that occasionally rolls over). The messaging protocol that uses this value disallows a 0. I need some thread-safe way to increment this counter (stored in a class field) every time I read it, which isn't hard if I store it as an int and use the Interlocked.Increment. However, I'm not sure how to incorporate skipping 0 into that. It's okay if I occasionally skip a few numbers; my output sequence doesn't have to be perfect. I cannot ever reuse the same number in any block of 4000. I would like to avoid using a lock.
Upvotes: 1
Views: 971
Reputation: 111860
This one:
Given:
static int value = ushort.MaxValue;
And in the code:
int temp, temp2;
do
{
temp = value;
temp2 = temp == ushort.MaxValue ? 1 : temp + 1;
}
while (Interlocked.CompareExchange(ref value, temp2, temp) != temp);
You'll have to use an int
and then cast it (for example in a get
property), because the Interlocked
aren't for all basic types.
We could probably make it a little faster in highly threaded contexts like this:
int temp = value;
while (true)
{
int temp2 = temp == ushort.MaxValue ? 1 : temp + 1;
int temp3 = Interlocked.CompareExchange(ref value, temp2, temp);
if (temp3 == temp)
{
break;
}
temp = temp3;
}
In this way we have to do one less read on failure.
As I've written in the comment, the central idea of this code is to increment in a temporary variable (temp2
) the counter, and then trying to exchange the old value that we know with the new value (Interlocked.CompareExchange
). If no one touched the old value in-between (Interlocked.CompareExchange() == temp
) then we have finished. If someone else incremented the value then we do another try. The ushort
is simulated by the use of an int
with a fixed maximum value (temp == ushort.MaxValue ? 1 : temp + 1
).
The second version, on failure of the Interlocked.CompareExchange()
reuses the value read by the function as the new basis on which to add 1.
The Interlocked.CompareExchange
used in that way can be used as a basis for building other Interlocked
operations (you want an Interlocked.Multiply
? You do a "standard" multiply and then try to Interlocked.CompareExchange
the old value)
Upvotes: 5