Ошибки, связанные с вызовом обработчиков событий в других потоках, возникают из-за того, что обработка состояния может изменяться между моментом, когда обработчик вызывается, и моментом, когда другой поток завершает работу. В вашем случае, если таймер и основной поток (где вы вызываете `sleep_for`) работают параллельно и используют одинаковые временные интервалы, существует вероятность того, что основной поток может завершиться раньше или в тот же момент, когда таймер вызывает обработчик события.
### Способы избежать ошибок:
1. **Используйте синхронизацию**: Вы можете использовать механизмы синхронизации, такие как мьютексы или семафоры, для обеспечения безопасного доступа к данным между потоками. Когда таймер вызывает свой обработчик, он может блокировать другой поток, пока обработчик завершает выполнение.
2. **Проверка состояния**: Как вы уже упомянули, возможно, стоит ввести проверку состояния, чтобы убедиться, что обработчики все еще существуют и корректны для использования. Это может быть реализовано через использование `std::shared_ptr` или `std::weak_ptr`.
3. **Вызов обработчика в основном потоке**: Вы можете создать очередь для событий и затем обрабатывать события в основном потоке.
Пример, показывающий использование очереди событий:
```cpp
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <queue>
#include <mutex>
#include <condition_variable>
class Timer {
public:
using Callback = std::function<void()>;
Timer(std::chrono::milliseconds duration) : duration_(duration), running_(false) {}
void Start() {
running_ = true;
thread_ = std::thread([this]() {
std::this_thread::sleep_for(duration_);
if (running_) {
std::unique_lock<std::mutex> lock(mutex_);
callbacks_.push_back(OnElapsed); // Добавим в очередь
cv_.notify_one(); // Оповестим основной поток
}
});
}
void Stop() {
running_ = false;
if (thread_.joinable()) {
thread_.join();
}
}
void Subscribe(const Callback& callback) {
std::lock_guard<std::mutex> lock(mutex_);
callbacks_.push_back(callback);
}
void ProcessEvents() {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [this] { return !callbacks_.empty(); });
// Вызов всех обработчиков
for (const auto& callback : callbacks_) {
callback();
}
callbacks_.clear();
}
private:
void OnElapsed() {
std::cout << "Timer elapsed!" << std::endl;
}
std::chrono::milliseconds duration_;
std::thread thread_;
std::vector<Callback> callbacks_;
bool running_;
std::mutex mutex_;
std::condition_variable cv_;
};
int main() {
Timer t(std::chrono::milliseconds(150));
t.Subscribe([]() { std::cout << "Event processed in main thread!" << std::endl; });
t.Start();
while (true) {
t.ProcessEvents(); // Обработка событий в основном потоке
}
return 0;
}
```
В этом примере обработчик события `OnElapsed` добавляется в очередь, и события обрабатываются в основном потоке. Это гарантирует, что в случае истечения времени не будет проблем с доступом к вызовам обработчиков, которые могут быть удалены или недоступны.
Таким образом, мы можем избежать ошибок, связанных с многопоточностью, обеспечив возможность безопасного вызова обработчиков.