Member 12902012 Ответов: 1

Использование std:: async ломает мою программу где-то еще, чтение нарушения доступа


Я работаю над приложением MFC, написанным на visual C++ в visual studio 2015. Я вызываю функцию объекта, используя указатель на него. Я получаю исключение "нарушение доступа чтения местоположения 0x4a522c31" здесь:

void Handler::NotifyEvent(I_Handler::eNotification nNotification){
	  for (t_InterfaceList::const_iterator iter = m_oClientList.begin(); iter != m_oClientList.end(); iter++){
	  (*iter)->I_Handler_OnEvent(nNotification);
	   }
    }


Полное сообщение об ошибке выглядит следующим образом: исключение, вызванное в 0x6ce42dc6 (ucrtbase.dll) в program.exe: 0xc000005: нарушение доступа к местоположению чтения 0x4a522c31. Это происходит при вызове функции I_Handler_OnEvent ().

Обработчик класса таков:

class Handler{
  typedef list<I_Handler*> t_InterfaceList;
  t_InterfaceList m_oClientList;
  static Handler* GetInstance();
  bool AddClient(I_Handler * pInterface);
  bool RemoveClient(I_Handler * pInterface);
  void OnTimer();
  void NotifyEvent(I_Handler::eNotification nNotification);
}

Класс I_Handler таков:
class I_Handler {
  public:
    virtual void I_Handler_OnEvent(eNotification nNotification) = 0;
};

Программное обеспечение работает следующим образом:

- Обработчик класса имеет список клиентов, которые относятся к классу I_Handler.
- Когда что-то происходит внутри OnTimer(), клиенты должны быть уведомлены.
- Чтобы уведомить их, внутри OnTimer() вызывается функция NotifyEvent ().
- NotifyEvent () перебирает список клиентов и вызывает каждую функцию I_Handler_OnEvent()

У меня есть класс где-то еще, который управляет окном (это CFormView), и он также является клиентом обработчика, что означает, что этот класс является чем-то вроде этого:
class View_Control : public CFormView, public I_Handler{
  DECLARE_DYNCREATE(View_Control)
  protected:
    View_Control();
    virtual ~View_Control();
  public:
    void I_Handler_OnEvent(eNotification nNotification) override;
}


std:: async используется для параллельного запуска задач в различных функциях внутри обработчика класса. Внутри функции OnTimer() Я проверяю готовность задачи с помощью wait_for для будущего объекта, возвращаемого std:: async. Когда я получаю std:: future_status:: ready, я вызываю NotifyEvent (), и все падает. Обратите внимание, что если я закомментирую вызов std::async, то задача не будет запущена и NotifyEvent() будет работать так, как задумано.

Что я уже пробовал:

Я проверил, и адрес указателя правильный, он указывает на объект класса 'View_Control', который зарегистрировался как клиент.

'AddClient' вызывается внутри функции ControlView, например:
Обработчик:: GetInstance () - >AddClient(this). Я проверил это и *iter, они всегда имеют разные значения, но близки (в одном исполнении *iter был 0x00595E78, а это было 0x00595DA8. Однако исключение всегда происходит при чтении одного и того же адреса, который нигде не находится рядом с этими указателями.

Похоже, что тот факт, что функция выполняется в другом потоке, созданном std::async, нарушает способность NotifyEvent() вызывать функцию-член указателем на объект.

Почему это происходит?

1 Ответов

Рейтинг:
2

Jochen Arndt

Классы MFC не являются потокобезопасными, но std::async вероятно, будет создан новый поток для выполнения функции, которая затем является причиной нарушения доступа при вызове функции-члена MFC из другого потока.

Общим решением для классов MFC является публикация пользовательских сообщений (WM_APP+xxx) и обработка их в ваших производных классах.

В вашем случае вы можете изменить список клиентов, чтобы он содержал дескрипторы окон элементов управления MFC вместо объектов, и использовать PostMessage для отправки определенного пользователем сообщения с кодом уведомления в каждое зарегистрированное окно. Цикл сообщений элементов управления, который выводится потоком графического интерфейса, затем вызывает обработчик для определенного пользователем сообщения.