Является ли мой скетч «потокобезопасным»?

Я работаю с .Net. Обычно я "блокирую" операции с целыми числами, когда у меня есть требования к потокобезопасности. Я не уверен, что это относится к моему скетчу здесь? Могу ли я обойти приращение одного из моих контактов прерывания?

Подводя итог, у меня есть 2 контакта прерывания - я хочу увеличить счетчик, который (каждые 5 секунд) записывает через последовательный порт и сбрасывает.

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

#include <Controllino.h>

const byte interruptPin0 = CONTROLLINO_IN0;
const byte interruptPin1 = CONTROLLINO_IN1;
int counter1 = 0;
int counter2 = 0;
unsigned long previousMillis = 0;
unsigned long interval = 5000;
int a = 60;
char buffer[256];

void setup()
{
  pinMode(interruptPin0, INPUT_PULLUP);
  pinMode(interruptPin1, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin0), pin1Fired, RISING);
  attachInterrupt(digitalPinToInterrupt(interruptPin1), pin2Fired, RISING);
  Serial.begin(9600);
}

void loop()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    sprintf(buffer,"A%d",counter1);
    Serial.println(buffer);
    sprintf(buffer,"D%d",counter2);
    Serial.println(buffer);
    counter1 = counter2 = 0;
  }
}

void pin1Fired()
{
  counter1++;
}

void pin2Fired()
{
  counter2++;
}

, 👍1


1 ответ


Лучший ответ:

4

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

Чтобы предотвратить гонку, нужно заблокировать прерывания, пока основной поток (все, что не выполняется в контексте прерывания) обращается к счетчики. Кстати, не забудьте сделать счетчики volatile.

Части кода, которые выполняются с отключенными прерываниями, называются «критические разделы», и вы должны сделать их как можно короче в чтобы ограничить задержку прерывания, которую они вводят. Как правило, в пределах эти критические разделы вы не делаете ничего, кроме доступа к общему переменные. Пример:

void loop()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    noInterrupts();  // начало критической секции
    int counter1Copy = counter1;
    counter1 = 0;
    interrupts();  // конец критической секции
    Serial.print("A");
    Serial.println(counter1Copy);
    noInterrupts();  // начало критической секции
    int counter2Copy = counter2;
    counter2 = 0;
    interrupts();  // конец критической секции
    Serial.print("D");
    Serial.println(counter2Copy);
  }
}
,

Отличный! Большое спасибо за это, вероятно, избавил меня от многих хлопот, когда дело дошло до пропущенных счетов! Только один вопрос - в то время, когда прерывания запрещены (?) - мы не уязвимы к отсутствию обработчиков прерываний? т.е. все еще отсутствуют подсчеты?, @JᴀʏMᴇᴇ

Когда прерывание происходит в это время, оно будет выполнено после того, как вы снова включите прерывания. Вы только потеряете счет, когда за это время произойдет 2 или более прерываний. Чтобы предотвратить это, вам нужно, чтобы отключенное время было ниже, чем время между двумя прерываниями., @chrisl

@chrisl прав. Чтобы быть более конкретным, вы теряете счет, если 2 или более экземпляров _одного и того же прерывания_ срабатывают в течение этого окна. Если вы получаете один INT0 и один INT1, все в порядке., @Edgar Bonet

Понял, блестяще объяснил. Спасибо Эдгар., @JᴀʏMᴇᴇ