Многопоточный код - принудительное выполнение порядка

У меня есть код, доступ к которому будет осуществляться из двух потоков:


class Timer{
public:
   void Start(){
      start_ = clock_->GetCurrentTime();
      running_ = true;
   }

   void Read(){
      if (running_){
         time_duration remaining = clock_->GetCurrentTime() - start_;
         Actions(remaining)
      }
   }

private:
   void Actions(time_duration remaining);
   time start_;
   bool running;
};

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

Метод Start () вызывается (только один раз) из одного потока. Метод Read () вызывается очень быстро из другого потока, вызовы начнут поступать до вызова Start ().

Очевидно, что очень важно, чтобы переменная start_ была инициализирована перед установлен флаг running_. Это можно решить, добавив мьютекс, который захватывается при вводе метода Start () ... и захватывается до того, как в методе Read () проверяется running_ ... но это кажется несколько ненужным. Если здесь все по порядку, то проблем нет. У меня нет проблем с тем фактом, что Read () может произойти, пока другой поток находится в маршрутизации Start (), например, получая время от часов ... Read () происходит достаточно быстро, что это просто не большое дело.

В любом случае, я искал способ гарантировать, что компилятор / процессор выполнит

  start_ = clock_->GetCurrentTime();
  running_ = true;

инструкции в том порядке, в котором они перечислены выше. (Или если я не замечаю чего-то еще).

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
388
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я не уверен, понимаю ли я ваш вопрос, но я думаю, вы хотите предотвратить выполнение Read, когда метод Start не установлен (или не был выполнен полностью)?

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

В методе Read вы можете указать, что метод Read должен ждать, если Auto / ManualResetEvent не был установлен, а в конце метода Start вы можете установить событие.

Вам нужно сделать start_ и running_ volatile, а затем ввести барьер памяти между двумя назначениями в Start().

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

teeks99 20.01.2009 20:16

Почему бы просто не приклеить

if (_running == false) Start();

В методе Read (). И либо защитите Start с помощью мьютекса, либо определите его как «критически важный», чтобы обеспечить его однопоточность.

Я не хочу запускаться каждый раз при вызове Read () ... в большинстве случаев при вызове Read () ничего не должно происходить.

teeks99 20.01.2009 20:21
Ответ принят как подходящий

Почему бы не избавиться от флага «работает» и использовать в качестве условия, пуста ли переменная «start»? Вы не указываете язык, но какой-то изменчивый маркер для «начала» тоже будет хорошей идеей. Это также предполагает, что "start" может быть записано атомарно. Например:

class Timer{
public:   
  void Start(){
    start_ = clock_->GetCurrentTime(); 
  }
  void Read(){
  if (nullptr != start_){
     time_duration remaining = clock_->GetCurrentTime() - start_;
     Actions(remaining)      
  }   
}
private:   
  void Actions(time_duration remaining);
  volatile time start_;
};

Это не совсем работает, потому что start_ - это не маленькая атомарная штука.

teeks99 21.01.2009 00:55

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

Stu Mackellar 21.01.2009 12:36

Язык - C++. Тип start_ - boost :: posix_time :: ptime. Я не верю, что запись объекта ptime является атомарной операцией, поэтому в приведенном выше примере проверка if при чтении (и, что более важно, строка после нее) может быть выполнена только с частичным значением в start_.

teeks99 21.01.2009 17:03

Ты прав. Вы всегда можете создать start_ в куче и вместо этого сохранить указатель на него. Однако в этой ситуации, вероятно, лучше использовать отдельный флаг. Однако применяется тот же принцип. Ключевое слово volatile всегда должно быть доступно в любом компиляторе C++, так что не беспокойтесь об этом.

Stu Mackellar 22.01.2009 00:05

Другие вопросы по теме