Состояние гонки при увеличении Int из потоков - PullRequest
1 голос
/ 07 июля 2011

У меня есть метод, который просто увеличивает целочисленное значение (i ++)

public void Calculate()
{
for(int i=0;i<1500;i++)
 y++;
}

Где Y - переменная класса Int.

Thread thr1 = new Thread(Calculate);
thr1.Start();   
Thread thr2 = new Thread(Calculate);
thr2.Start();
Thread thr3 = new Thread(Calculate);
thr3.Start();
Thread thr4 = new Thread(Calculate);
thr4.Start();

При запуске 4 Threads с Calculate Delegate значение Y должно быть 6000, если Y начинается со значения 0 Но не всегда он становится 6000, иногда я получаю 5993 или 6003, поэтому бывают случаи, когда это значение не является тем, которое по логике должно быть. Так что есть какое-то решение, чтобы предотвратить это, я не хочу блокировать Y, когда поток увеличивается, так есть ли способ установить параллельное значение переменной из нескольких потоков?

РЕДАКТИРОВАТЬ : он работает с Interlock.Increment();, но он замедляет алгоритм, поэтому то, что делает его правильно и быстрее, это:

int i = 0;
int j = 0;
for (i = 0; i < 1500; i++)
{
j++;
this.label1.Text = y.ToString();
}
lock(y)
{
    y += j;
}

Ответы [ 4 ]

9 голосов
/ 07 июля 2011

Это состояние гонки.Вам нужно использовать Interlocked.Increment, чтобы сделать это безопасным способом.

2 голосов
/ 07 июля 2011

Вы испытываете не потерю точности, а результат небезопасного способа доступа к переменной. Поскольку эта переменная является общей для разных потоков, необходимо убедиться, что операция приращения равна atomic . Когда поток A считывает значение поля, вы должны убедиться, что никакой другой поток не может изменить его до того, как поток A вернет увеличенное значение обратно.

Чтобы заблокировать выполнение других потоков, используйте статический метод Interlocked.Increment (ref Int32) , чтобы атомно увеличить значение поля.

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

1 голос
/ 07 июля 2011

Вы можете создать новый объект y со значением int, поэтому вы можете заблокировать объект y, когда поток захочет увеличить его. Это делает Calculate () потокобезопасным.

            public void Calculate()
        {                
            for(int i = 0; i < 1500; i++)
            {
                lock (y)
                {
                    y.value++;
                }
            }
        }

Ключевое слово lock гарантирует, что один поток не войдет в критический раздел кода, в то время как другой поток находится в критическом разделе. Если другой поток попытается ввести заблокированный код, он будет ждать блокировки до тех пор, пока объект не будет освобожден.

http://msdn.microsoft.com/en-us/library/c5kehkcz%28v=VS.100%29.aspx

0 голосов
/ 07 июля 2011

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

Из документов:

Ключевое слово volatile указывает, что поле может быть изменено несколькими потоками, которые выполняются одновременно.Поля, которые объявлены как volatile, не подлежат оптимизации компилятора, которая предполагает доступ из одного потока.Это гарантирует, что в поле всегда присутствует самое последнее значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...