18

我想从多个线程中增加一个无符号整数。

我知道 Interlocked.Increment,但它不处理无符号整数。我可以使用 lock(),但出于性能原因,我宁愿不使用。

仅以正常方式递增它是线程安全的吗?偶尔的增量是否丢失也没关系,因为它仅用于统计。我不想要的是被破坏的价值。

4

6 回答 6

45

您说出于性能原因不想使用lock- 但您测试过吗?一个无争议的锁(这很可能是,从它的声音)是相当便宜的。

在线程方面(一般来说,尤其是线程方面),我通常会选择“显然正确”而不是“聪明且可能性能更好”。

对您的应用程序有无锁定进行基准测试,看看您是否能注意到差异。如果锁定会产生重大影响,那么可以肯定的是,使用狡猾的东西。否则,我只会坚持使用锁。

可能想要做的一件事是Interlocked.Increment与 an 一起使用,int并在需要时将其转换为 a uint,如下所示:

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());
    }

}

输出:

2147483647
2147483648
2147483649

(换句话说,它包装没有问题。)

于 2009-06-01T12:46:26.950 回答
11

如果您确实需要完整范围的无符号整数 (2^32 - 1) 而不是有符号整数 (2^31 -1),则可以转换为 int64(存在Interlocked.Increment采用 int64 的重载),然后再转换回到一个无符号整数。

于 2009-06-01T12:32:30.567 回答
2

pre-kidney's answer 的基础上,您可以创建自己的助手类。由于增量将在二进制级别上以相同的方式工作,因此您可以在使用类递增之前将类型从无符号更改为有符号Unsafe

using System.Runtime.CompilerServices;
using System.Threading;

public static class InterlockedEx
{
    /// <summary>
    /// unsigned equivalent of <see cref="Interlocked.Increment(ref Int32)"/>
    /// </summary>
    public static uint 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);
    }
}
于 2019-12-01T17:17:55.660 回答
2

从 .NET 5.0Interlocked.Increment开始,无符号和有符号int32以及int64.

https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=net-5.0

于 2021-02-04T12:21:42.710 回答
0

在使用带符号整数的二进制补码表示的系统上(根据维基百科,“几乎全部” ),递增无符号整数与递增使用同一组位表示的有符号整数具有相同的效果。因此,可以在无符号整数上使用 InterlockedIncrement 而不牺牲任何东西。

例如,对于 3 位,我们有下表:

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

在这两种情况下,加一(并考虑溢出)相当于在表中向下移动一个条目。请注意,这不适用于补码算术,因为负数以相反的顺序排列。

于 2018-09-25T06:27:25.400 回答
-15

您可以将 uint 声明为 volatile。

http://msdn.microsoft.com/en-us/library/x13ttww7(VS.71).aspx

于 2009-06-01T12:31:27.493 回答