C # многопоточное беззнаковое приращение - PullRequest
13 голосов
/ 01 июня 2009

Я хочу увеличить целое число без знака из нескольких потоков.

Я знаю о Interlocked.Increment, но он не обрабатывает целые числа без знака. Я мог бы использовать lock (), но я бы предпочел не по возможности из соображений производительности.

Является ли потокобезопасным просто увеличивать его обычным способом? Не имеет значения, если случайный прирост был потерян, так как он используется только для статистики. Чего я не хочу, так это значения, которое будет повреждено.

Ответы [ 4 ]

39 голосов
/ 01 июня 2009

Вы говорите, что не хотите использовать lock по соображениям производительности, но проверяли ли вы его? Неоспоримая блокировка (которая, вероятно, будет, по звукам) довольно дешевая.

Я обычно говорю «очевидно правильно», а не «умно и, возможно, лучше работает», когда речь идет о многопоточности (и в целом, но особенно при многопоточности).

Оцените приложение с блокировкой и без нее и посмотрите, сможете ли вы заметить разницу. Если блокировка делает значительную разницу, то обязательно используйте хитрые вещи. В противном случае я бы просто придерживался замка.

Одна вещь, которую вы могли бы хотеть сделать, это использовать Interlocked.Increment с int и просто разыграть ее, когда необходимо, чтобы получить 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

(Другими словами, без проблем.)

10 голосов
/ 01 июня 2009

Если вам действительно нужен полный диапазон целых чисел без знака (2 ^ 32 - 1), а не целого числа со знаком (2 ^ 31 -1), вы можете привести к int64 (существует перегрузка Interlocked.Increment, которая принимает int64), а затем приведите обратно к неподписанному int.

0 голосов
/ 25 сентября 2018

В системах, использующих двойное представление представления целых чисел со знаком ( «практически все» , согласно википедии), увеличение целого числа без знака имеет тот же эффект, что и увеличение целого числа со знаком, представленного с использованием того же набора битов. Таким образом, можно использовать 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

Увеличение на единицу (и с учетом переполнения) эквивалентно перемещению вниз на одну запись в таблице, в обоих случаях. Обратите внимание, что это не работает для арифметики, дополняющей, поскольку негативы располагаются в обратном порядке.

0 голосов
/ 01 июня 2009

вы можете объявить uint как volatile.

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

...