У меня есть код, доступ к которому будет осуществляться из двух потоков:
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;
инструкции в том порядке, в котором они перечислены выше. (Или если я не замечаю чего-то еще).





Я не уверен, понимаю ли я ваш вопрос, но я думаю, вы хотите предотвратить выполнение Read, когда метод Start не установлен (или не был выполнен полностью)?
В этом случае вы не можете решить свою проблему с помощью AutoResetEvent или ManualResetEvent (в зависимости от того, какое поведение вы хотите).
В методе Read вы можете указать, что метод Read должен ждать, если Auto / ManualResetEvent не был установлен, а в конце метода Start вы можете установить событие.
Вам нужно сделать start_ и running_ volatile, а затем ввести барьер памяти между двумя назначениями в Start().
Почему бы просто не приклеить
if (_running == false) Start();
В методе Read (). И либо защитите Start с помощью мьютекса, либо определите его как «критически важный», чтобы обеспечить его однопоточность.
Я не хочу запускаться каждый раз при вызове Read () ... в большинстве случаев при вызове Read () ничего не должно происходить.
Почему бы не избавиться от флага «работает» и использовать в качестве условия, пуста ли переменная «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_ - это не маленькая атомарная штука.
Мы действительно приближаемся к тому моменту, когда необходимо будет указать язык, чтобы двигаться дальше. Вышеупомянутое, вероятно, сработает, потому что все, что нас волнует, - это то, что значение start_ больше не равно 0, нам не нужно точно знать его новое значение.
Язык - C++. Тип start_ - boost :: posix_time :: ptime. Я не верю, что запись объекта ptime является атомарной операцией, поэтому в приведенном выше примере проверка if при чтении (и, что более важно, строка после нее) может быть выполнена только с частичным значением в start_.
Ты прав. Вы всегда можете создать start_ в куче и вместо этого сохранить указатель на него. Однако в этой ситуации, вероятно, лучше использовать отдельный флаг. Однако применяется тот же принцип. Ключевое слово volatile всегда должно быть доступно в любом компиляторе C++, так что не беспокойтесь об этом.
Я думал что-то в этом роде ... но я искал немного более подробностей. Все компиляторы поддерживают volatile? Вы знаете какую-нибудь хорошую документацию о барьерах памяти?