Bonjour !
J'ai un service qui expose une classe COM à des clients.
La classe COM lit des données pour les fournir à ses clients.
Les données sont en lecture seule et sont mises à jour de temps en temps par d'autres processus, en bloc.
Je souhaite changer les données à la volée sans arrêter le service.
Supposons que les données soient gérées par une classe DataLinker:
Voici la classe DataLocker qui maintient les données en disponibilité, tant qu'au
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 class DataLinker { public: DataLinker(std::_tstring); bool Open(); ... };
moins un client les utilise ou tant qu'il n'y a pas de nouvelles données disponibles,
grâce à un compteur de référence:
Voici la classe singleton DataSwitcher qui gère la disponibilité et le changement de données (un pooler avec deux DataLocker pour passer de l'un à l'autre):
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 class DataLocker { public: DataLocker() = default; private: DataLocker(DataLocker const &) = delete; DataLocker & operator=(DataLocker const &) = delete; DataLinker *f_ptr_DataLinker = nullptr; std::_tstring f_datapath; LONG f_refcnt = 0l; public: DataLinker *GetDataLinker() { return f_ptr_DataLinker; } std::_tstring const & GetDataPath() const { return f_datapath; } bool IsEmpty() const { return f_ptr_DataLinker == nullptr; } void AddRef() { InterlockedIncrement(&f_refcnt); } void Release() { if (InterlockedDecrement(&f_refcnt) == 0) Unload(); } bool Load(std::_tstring const & datapath) { ATLASSUME(f_ptr_DataLinker == nullptr && f_refcnt == 0l); DataLinker *ptr_DataLinker; try { ptr_DataLinker = new DataLinker(datapath); if (ptr_DataLinker == nullptr || !ptr_DataLinker->Open()) { delete ptr_DataLinker; return false; } f_ptr_DataLinker = ptr_DataLinker; f_refcnt = 1l;//Increment(&f_refcnt); f_datapath = datapath; return true; } catch (std::exception & e) { #ifdef _DEBUG OutputDebugStringA(e.what()); #endif delete ptr_DataLinker; } return false; } private: void Unload() { ATLASSUME(f_ptr_DataLinker != nullptr && f_refcnt == 0l); delete f_ptr_DataLinker; f_ptr_DataLinker = nullptr; //f_refcnt = 0l; f_datapath.clear(); } };
Et pour finir voici le code à ajouter dans la classe COM, ou en début et fin d'utilisation de n'importe quel client:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 class DataSwitcher { private: DataLocker f_pls1; DataLocker f_pls2; DataLocker *f_curpls = nullptr; DataSwitcher() { CheckParams(); } public: void CheckParams(); static DataSwitcher & Get(); DataLocker *Data() { return f_curpls; } }; void DataSwitcher::CheckParams() { //check if a DataLocker is available DataLocker *next_pls = nullptr; DataLocker *curr_pls = nullptr; if (f_pls1.IsEmpty()) { next_pls = &f_pls1; curr_pls = &f_pls2; } else if (f_pls2.IsEmpty()) { next_pls = &f_pls2; curr_pls = &f_pls1; } if (next_pls) { unsigned somevalue = read_somevalue_somewhere(); std::_tstring datapath(_T("D:\\DATA_VERSION_") + std::to_tstring(somevalue) + _T("\\");); if (datapath != curr_pls->GetDataPath()) { //load... if (next_pls->Load(datapath)) { //... and switch f_curpls = next_pls; //release current if (!curr_pls->IsEmpty()) curr_pls->Release(); } } } } DataSwitcher & DataSwitcher::Get() { static DataSwitcher ds; return ds; }
La méthode DataSwitcher::CheckParams() est appélée une fois lors de la construction unique de DataSwitcher dans sa méthode static Get(), et puis de temps en temps dans un background thread qui appelle CheckParams().
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 DataLocker *f_curpls = nullptr; ... HRESULT FinalConstruct() { f_curpls = DataSingleton::Get().Data(); if (f_curpls == nullptr) return E_FAIL; f_curpls->AddRef(); return S_OK; } void FinalRelease() { if (f_curpls) f_curpls->Release(); }
J'ai trois doutes:
-le compteur de référence de DataLocker est-il safe, ne va-t-il jamais passer sous zéro, ou rester toujours strictement supérieur à zéro ?
-Est-ce que l'échange de DataLocker dans CheckParams() est safe, ainsi que son acquisition et incrémentation de référence dans le FinalConstruct() des clients ?
-En fin de programme, il reste un DataLocker disponible. Où faire le dernier Release() proprement ?
Voilà, merci, j'espère que j'ai été assez clair
Partager