问题
I want to increment an unsigned integer from multiple threads.
I know about Interlocked.Increment, but it does not handle unsigned integers. I could use lock(), but I would rather not if possible for performance reasons.
Is it thread safe just to increment it in the normal way? It would not matter if the occasional increment got lost, as it's only used for statistics. What I don't want is the value to get corrupted.
回答1:
You say you don't want to use lock
for performance reasons - but have you tested it? An uncontested lock (which this is likely to be, by the sounds of it) is pretty cheap.
I generally go for "obviously correct" rather than "clever and possibly better performing" when it comes to threading (and in general, but especially for threading).
Benchmark your app with and without locking, and see whether you can even notice the difference. If locking makes a significant difference then sure, use cunning stuff. Otherwise, I'd just stick with a lock.
One thing you might want to do is use Interlocked.Increment
with an int
and just cast it when necessary to get a uint
, like this:
using System;
using System.Reflection;
using System.Threading;
public class Test
{
private static int count = int.MaxValue-1;
public static uint IncrementCount()
{
int newValue = Interlocked.Increment(ref count);
return unchecked((uint) newValue);
}
public static void Main()
{
Console.WriteLine(IncrementCount());
Console.WriteLine(IncrementCount());
Console.WriteLine(IncrementCount());
}
}
Output:
2147483647
2147483648
2147483649
(In other words it wraps with no problems.)
回答2:
If you really need the full range of an unsigned int (2^32 - 1) rather than a signed int (2^31 -1), you could cast to an int64 (there is an Interlocked.Increment
overload that takes int64) and then cast back to an unsigned int.
回答3:
Building on pre-kidney's answer, you can create your own helper class. Since the increment will work in the same way on a binary level, you can just change the type from unsigned to signed before incrementing with the Unsafe
class:
using System.Runtime.CompilerServices;
using System.Threading;
public static class InterlockedEx
{
/// <summary>
/// unsigned equivalent of <see cref="Interlocked.Increment(ref Int32)"/>
/// </summary>
public static ulong Increment(ref uint location)
{
int incrementedSigned = Interlocked.Increment(ref Unsafe.As<uint, int>(ref location));
return Unsafe.As<int, uint>(ref incrementedSigned);
}
/// <summary>
/// unsigned equivalent of <see cref="Interlocked.Increment(ref Int64)"/>
/// </summary>
public static ulong Increment(ref ulong location)
{
long incrementedSigned = Interlocked.Increment(ref Unsafe.As<ulong, long>(ref location));
return Unsafe.As<long, ulong>(ref incrementedSigned);
}
}
回答4:
On systems using a twos complement representation of signed integers ("virtually all", according to wikipedia) incrementing an unsigned integer has the same effect as incrementing a signed integer represented using the same set of bits. Thus, one can use InterlockedIncrement on unsigned integers without sacrificing anything.
For example, with 3 bits we have the following table:
raw bits | unsigned integer | twos complement signed integer
------------------------------------------------------------
000 | 0 | 0
001 | 1 | 1
010 | 2 | 2
011 | 3 | 3
100 | 4 | -4
101 | 5 | -3
110 | 6 | -2
111 | 7 | -1
Incrementing by one (and taking overflow into account) is equivalent to moving down one entry in the table, in both cases. Note that this doesn't work for ones complement arithmetic, since the negatives are arranged in the opposite order.
回答5:
you can declare the uint as volatile.
http://msdn.microsoft.com/en-us/library/x13ttww7(VS.71).aspx
来源:https://stackoverflow.com/questions/934619/c-sharp-multi-threaded-unsigned-increment