Мне нужна ваша помощь с wxWidgets. У меня есть 2 потока (1 wxTimer и 1 wxThread), мне нужно установить связь между этими двумя потоками. У меня есть класс, который содержит методы чтения/записи переменных в этом классе. (Совместное использование памяти с этим объектом)
Моя проблема заключается в следующем: я инициирую "новый" этот класс в одном потоке, но я не знаю, что необходимо во втором потоке. Потому что, если instanciate тоже, адрес переменной является разным, и мне нужно общаться, поэтому мне нужно значение в переменной:/
Я знаю о необходимости wxSemaphore, чтобы предотвратить ошибку при доступе в одно и то же время.
Спасибо вам за помощь!
EDIT: Мой код
Итак, мне нужно сделать ссылку с моим кодом. Спасибо вам всем;)
Это мое объявление для моего wxTimer в моем классе: EvtFramePrincipal (IHM)
В.h
EvtFramePrincipal( wxWindow* parent );
#include <wx/timer.h>
wxTimer m_timer;
в.cpp -Constructor EvtFramePrincipal
EvtFramePrincipal::EvtFramePrincipal( wxWindow* parent )
:
FramePrincipal( parent ),m_timer(this)
{
Connect(wxID_ANY,wxEVT_TIMER,wxTimerEventHandler(EvtFramePrincipal::OnTimer),NULL,this);
m_timer.Start(250);
}
Поэтому я вызываю метод OnTimer каждые 250 мс с этой строкой.
Для моего второго потока начинаем с EvtFramePrincipal (IHM):
в.h EvtFramePrincipal
#include "../Client.h"
Client *ClientIdle;
в.cpp EvtFramePrincipal
ClientIdle= new Client();
ClientIdle->Run();
В.h Client (Thread)
class Client: public wxThread
public:
Client();
virtual void *Entry();
virtual void OnExit();
В.cpp Client (Thread)
Client::Client() : wxThread()
{
}
Итак, здесь нет проблем, нить в порядке? Теперь мне нужен этот класс, который используется как посланник между моими 2 потоками.
#ifndef PARTAGE_H
#define PARTAGE_H
#include "wx/string.h"
#include <iostream>
using std::cout;
using std::endl;
class Partage
{
public:
Partage();
virtual ~Partage();
bool Return_Capteur_Aval()
{ return Etat_Capteur_Aval; }
bool Return_Capteur_Amont()
{ return Etat_Capteur_Amont; }
bool Return_Etat_Barriere()
{ return Etat_Barriere; }
bool Return_Ouverture()
{ return Demande_Ouverture; }
bool Return_Fermeture()
{ return Demande_Fermeture; }
bool Return_Appel()
{ return Appel_Gardien; }
void Set_Ouverture(bool Etat)
{ Demande_Ouverture=Etat; }
void Set_Fermeture(bool Etat)
{ Demande_Fermeture=Etat; }
void Set_Capteur_Aval(bool Etat)
{ Etat_Capteur_Aval=Etat; }
void Set_Capteur_Amont(bool Etat)
{ Etat_Capteur_Amont=Etat; }
void Set_Barriere(bool Etat)
{ Etat_Barriere=Etat; }
void Set_Appel(bool Etat)
{ Appel_Gardien=Etat; }
void Set_Code(wxString valeur_code)
{ Code=valeur_code; }
void Set_Badge(wxString numero_badge)
{ Badge=numero_badge; }
void Set_Message(wxString message)
{
Message_Affiche=wxT("");
Message_Affiche=message;
}
wxString Get_Message()
{
return Message_Affiche;
}
wxString Get_Code()
{ return Code; }
wxString Get_Badge()
{ return Badge; }
protected:
private:
bool Etat_Capteur_Aval;
bool Etat_Capteur_Amont;
bool Etat_Barriere;
bool Demande_Ouverture;
bool Demande_Fermeture;
bool Appel_Gardien;
wxString Code;
wxString Badge;
wxString Message_Affiche;
};
#endif // PARTAGE_H
Поэтому в моем EvtFramePrincipal (wxTimer) я создаю новый для этого класса. Но в другом потоке (wxThread), что мне нужно сделать, чтобы общаться?
Если трудно понять, так жаль:/
Затем основной поток должен создать сначала общую переменную. После этого вы можете создать оба потока и передать им указатель на общую переменную.
Итак, оба они знают, как взаимодействовать с общей переменной. Вам необходимо реализовать mutex или wxSemaphore в методах общей переменной.
Вы можете использовать синглтон, чтобы получить доступ к центральному объекту.
Кроме того, создайте центральный объект перед созданием потоков и передайте ссылку на центральный объект на потоки.
Используйте мьютекс в центральном объекте для предотвращения одновременного доступа.
Создание одного центрального объекта в каждом потоке не является вариантом.
Начнем с некоторых предположений. ОП указала, что
У меня есть 2 потока (1 wxTimer и 1 wxThread)
Честно говоря, я очень мало знаю о структуре wxWidgets, но всегда есть документация. Поэтому я вижу, что:
wxTimer::Notify()
когда истечет таймер. Документация ничего не говорит о выполнении потока (хотя есть примечание. Таймер может использоваться только из основного потока, который я не уверен, как понимать). Я могу догадаться, что мы должны ожидать, что метод Notify будет выполняться в потоке или потоках цикла цикла или цикла таймера.wxThread::Entry()
. Запуск объекта wxThread фактически создаст поток, который запускает метод Entry. Таким образом, ваша проблема в том, что вам нужен тот же объект для доступа как в wxTimer::Notify()
и в wxThread::Entry()
.
Этот объект:
Это не одна переменная, а большая часть этого магазина в одном классе
например
struct SharedData {
// NOTE: This is very simplistic.
// since the information here will be modified/read by
// multiple threads, it should be protected by one or more
// mutexes
// so probably a class with getter/setters will be better suited
// so that access with mutexes can be enforced within the class.
SharedData():var2(0) { }
std::string var1;
int var2;
};
из которых у вас есть пример:
std::shared_ptr<SharedData> myData=std::make_shared<SharedData>();
или, возможно, в форме указателя или, возможно, в качестве локальной переменной или атрибутом объекта
Вы действительно не используете wxTimer
или wxThread
, но классы, наследующие от них (по крайней мере, wxThread::Entry()
является чисто виртуальным. В случае wxTimer
вы можете изменить владельца на другой wxEvtHandler
, который получит событие, но вам все равно необходимо обеспечить реализацию.
Таким образом, вы можете
class MyTimer: public wxTimer {
public:
void Notify() {
// Your code goes here
// but it can access data through the local reference
}
void setData(const std::shared_ptr<SharedData> &data) {
mLocalReference=data
}
private:
std::shared_ptr<SharedData> mLocalReferece
};
Это необходимо будет установить:
MyTimer timer;
timer.setData(myData);
timer.StartOnece(10000); // wake me up in 10 secs.
Аналогично для потока
class MyThread: public wxThread {
public:
void Entry() {
// Your code goes here
// but it can access data through the local reference
}
void setData(const std::shared_ptr<SharedData> &data) {
mLocalReference=data
}
private:
std::shared_ptr<SharedData> mLocalReferece
};
Это необходимо будет установить:
MyThread *thread=new MyThread();
thread->setData(myData);
thread->Run(); // threads starts running.
Иногда вы не можете изменять MyThread или MyTimer... или слишком сложно перенаправить ссылку на myData на поток или экземпляры таймера... или вы слишком ленивы или слишком заняты, чтобы беспокоиться (остерегайтесь вашего технического долга ! !)
Мы можем настроить SharedData на:
struct SharedData {
std::string var1;
int var2;
static SharedData *instance() {
// NOTE that some mutexes are needed here
// to prevent the case where first initialization
// is executed simultaneously from different threads
// allocating two objects, one of them leaked.
if(!sInstance) {
sInstance=new SharedData();
}
return sInstance
}
private:
SharedData():var2(0) { } // Note we've made the constructor private
static SharedData *sInstance=0;
};
Этот объект (поскольку он позволяет создавать только один объект) может быть доступен из MyTimer::Notify()
или MyThread::Entry()
с помощью
SharedData::instance()->var1;
(или почему легкое решение может укусить вас в будущем).
Мои главные причины:
Тем не менее, я не думаю, что можно полностью избежать. Он имеет свои возможности, он может решить вашу проблему, и это может сэкономить ваш день.
Вы все равно можете организовать свои данные в центральном репозитории с помощью методов доступа к различным экземплярам (или различным реализациям) данных.
Этот центральный репозиторий может быть одноточечным (он действительно является центральным, распространенным и уникальным), но не является общими данными, а используется для извлечения общих данных, например, идентифицированных с помощью некоторого идентификатора (который может быть проще распределить между потоки с использованием опции 1)
Что-то вроде:
CentralRepository::instance()->getDataById(sharedId)->var1;
Кажется, что ваш объект EvtFramePrincipal
выполнит как обратный вызов таймера, так и будет содержать указатель ClientIdle для объекта Client (поток)... Я бы сделал: