Простой вопрос о потоках, блокировка нелокальных изменений - PullRequest
0 голосов
/ 27 января 2011

Хорошо, сначала я должен предвосхитить этот вопрос с заявлением об ограничении ответственности, я действительно новичок в потоках, так что это может быть вопрос новичка, но я искал в Google и не смог найти ответ.Насколько я понимаю, критический раздел - это код, к которому могут обращаться два или более потоков, опасность состоит в том, что один поток перезапишет значение до завершения другого, и наоборот.Что вы можете сделать, например, с изменениями, внесенными за пределы вашего класса, у меня есть программа мониторинга линии:

int currentNumber = provider.GetCurrentNumber();
if(provider.CanPassNumber(false, currentNumber))
{
currentNumber++;
provider.SetNumber(currentNumber);
}

, а в другом потоке у меня что-то вроде этого:

if(condition)
    provider.SetNumber(numberToSet);

СейчасЯ боюсь, что в первой функции я получаю currentNumber, который равен 5, сразу после этого в другом потоке число устанавливается равным 7, а затем оно перезаписывает 7 на 6, игнорируя изменения, сделанные потоком, который установил его в 7.

Есть ли какой-либо способ заблокировать provider.SetNumber до завершения первой функции?Критическим разделом является в основном currentNumber, который может быть изменен многими местами в программе.

Надеюсь, я дал понять, если не скажу, и постараюсь объяснить себя лучше.

РЕДАКТИРОВАТЬ: Также я сделал функции очень короткими для примера.На самом деле функция намного длиннее и многократно вносит изменения в currentNumber, поэтому я не хочу блокировать всю функцию.Если я блокирую все вызовы provider.SetNumber и освобождаю его после завершения, он может измениться во время его освобождения, прежде чем я снова заблокирую его для вызова provider.SetNumber.Честно говоря, я также беспокоюсь о блокировке всей функции из-за производительности и тупика.

Ответы [ 6 ]

2 голосов
/ 27 января 2011

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

Есть несколько способов, которые вас интересуют: Обмен и Чтение , оба из которых являются поточно-ориентированными.

0 голосов
/ 27 января 2011

В вашем методе SetNumber вы можете просто использовать оператор блокировки:

public class MyProvider {
     object numberLock = new object();
     ...
     public void SetNumber(int num) {
          lock(numberLock) {
               // Do Stuff
          }
     }
}

Также обратите внимание, что в вашем примере currentNumber является примитивом (int), что означает, что значение переменной неперезаписывается при изменении значения фактического члена данных вашего провайдера.

0 голосов
/ 27 января 2011

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

Создание критическогораздел прост

Lock(this)
{
    //Only one thread can run this at a time
}

примечание: этот должен быть заменен каким-то внутренним объектом ...

0 голосов
/ 27 января 2011

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

// Define an object which can be locked in your class.
object locker = new object();

// Add around your critical sections the following :
lock (locker) { /* ... */ }

Это изменит вашкод для:

int currentNumber = provider.GetCurrentNumber();

lock (locker)
{
  if(provider.CanPassNumber(false, currentNumber))
  {
    currentNumber++;
    provider.SetNumber(currentNumber);
  }
}

и:

if(condition)
{
  lock (locker)
  {
    provider.SetNumber(numberToSet);
  }
}
0 голосов
/ 27 января 2011

Как сказал Филипп, блокировка здесь полезна.Вы должны блокировать не только ProviderSetNumber (currentNumber), но и любое условие, от которого зависит установщик.

lock(someObject)
{
  if(provider.CanPassNumber(false, currentNumber))
  {
    currentNumber++;
    provider.SetNumber(currentNumber);
  }
}

, а также

if(condition)
{
  lock(someObject)
  {
    provider.SetNumber(numberToSet);
  }
}

Еслизависит от numberToSet, вы должны взять оператор блокировки вокруг всего блока.Также обратите внимание, что someObject должен быть таким же объектом.

0 голосов
/ 27 января 2011

Вы хотите посмотреть ключевое слово Lock.Также вы можете захотеть, чтобы этот учебник Threading в C # .

...