2013-06-19 3 views
4

Я написал некоторый код потока с тем, что кажется неправильным предположением, что целые числа были потокобезопасными. Теперь кажется, что, хотя они и есть, мое использование их НЕ является потокобезопасным. Я использую глобальное целое число ThreadCount для хранения количества потоков. Во время создания потока я увеличиваю ThreadCount. Во время уничтожения потока я уменьшаю его. После того, как все потоки создаются, я жду их выполнения (ThreadCount должен упасть до 0), а затем написать мой окончательный отчет и выйти.Нитки завершены, но цикл не заканчивается

Иногда (5%), хотя я никогда не получаю 0, хотя посмертное обследование моего журнала показывает, что все потоки выполнялись и заканчивались. Таким образом, все признаки указывают на тотализацию ThreadCount. Я говорил себе, что это невозможно, поскольку это целое число, и я просто использую inc/dec.

Вот какой-то соответствующий код:

var // global 
    ThreadCount : integer;   // Number of active threads 
... 

constructor TTiesUpsertThread.Create(const CmdStr : string); 
begin 
    inherited create(false); 
    Self.FreeOnTerminate := true; 
... 
    Inc(ThreadCount);  // Number of threads created. Used for throttling. 
end; 

destructor TTiesUpsertThread.Destroy; 
begin 
    inherited destroy; 
    Dec(ThreadCount);  // When it reaches 0, the overall job is done. 
end; 

... 
//down at the end of the main routine: 

    while (ThreadCount > 0) do // Sometimes this doesn't ever end. 
    begin 
     SpinWheels('.'); // sleeps for 1000ms and writes dots... to console 
    end; 

Я думаю, что моя проблема с INC/DEC. Я думаю, что я получаю столкновений, когда два или более dec() попадают одновременно и оба читают одно и то же значение, поэтому они заменяют его одинаковым значением. ex: ThreadCount = 5, а два потока завершаются одновременно, оба считываются 5, заменяются на 4. Но новое значение должно быть равно 3.

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

Если это моя проблема, я использую критический выбор для защиты inc/dec?
Спасибо, что посмотрели.

+4

Увы, у меня нет абсолютно никаких фактов, чтобы поддержать его, я считаю, что 'Inc' и' Dec' не являются атомарными, особенно, особенно если у вас есть проверка диапазона. Возможно, вам стоит взглянуть на функцию WinAPI 'InterlockedIncrement (..) 'или использовать' TCriticalSection' или (возможно, быстрее) 'TMultiReadExclusiveWriteSynchronizer' –

+7

Чтобы заблокировать все ваши потоки, вы можете использовать' WaitForMultipleObjects'. Тогда вам не нужен счетчик. Он возвращается, когда все объекты сигнализируются (или время истекает). –

+0

Вы изучали OmniThreadLibrary? Возможно, ThreadCount может быть просто свойством контроллера, скорее, как подверженная ошибкам переменная, подсчитанная вручную –

ответ

7

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

  1. Переменная считывается в регистр.
  2. Модификация произведена в реестре.
  3. Новое значение возвращается к переменной.

Это чтение/изменение/запись не является атомарным. Если у вас есть два потока, выполняемых одновременно, у вас есть каноническая гонка данных.

  • Нить 1 считывает значение, N говорит.
  • Резьба 2 считывает значение, то же значение, которое было прочитано нитью 1, N.
  • Нить 1 записывает N + 1 в переменную.
  • Резьба 2 записывает N + 1 в переменную.

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

В этом случае нет необходимости в критической секции с полным взрывом. Используйте InterlockedIncrement для выполнения незакрепленной, потокобезопасной модификации.

+1

Awesome, спасибо. Я нашел здесь хороший пример: http://stackoverflow.com/questions/4016919/thread-safe-way-to-increment-and-return-an-integer-in-delphi/4016962#4016962 –

Смежные вопросы