Una segunda forma de eliminar el problema de colisión de
tareas sobre recursos compartidos es la eliminación de las
variables compartidas, lo cual puede realizarse mediante la
creación de diferentes almacenamientos para la misma variable,
uno por cada hilo que use el objeto. De esta forma, si tiene
cinco hilos que usan un objeto con una variable x, el
almacenamiento local al hilo genera automáticamente cinco
porciones de memoria distintas para almacenar
x. Afortunadamente, la creación y gestión del almacenamiento
local al hilo la lleva a cabo una plantilla de ZThread
llamada ThreadLocal
, tal y como se
puede ver aquí:
//: C11:ThreadLocalVariables.cpp {RunByHand} // Automatically giving each thread its own storage. //{L} ZThread #include <iostream> #include "zthread/Thread.h" #include "zthread/Mutex.h" #include "zthread/Guard.h" #include "zthread/ThreadedExecutor.h" #include "zthread/Cancelable.h" #include "zthread/ThreadLocal.h" #include "zthread/CountedPtr.h" using namespace ZThread; using namespace std; class ThreadLocalVariables : public Cancelable { ThreadLocal<int> value; bool canceled; Mutex lock; public: ThreadLocalVariables() : canceled(false) { value.set(0); } void increment() { value.set(value.get() + 1); } int get() { return value.get(); } void cancel() { Guard<Mutex> g(lock); canceled = true; } bool isCanceled() { Guard<Mutex> g(lock); return canceled; } }; class Accessor : public Runnable { int id; CountedPtr<ThreadLocalVariables> tlv; public: Accessor(CountedPtr<ThreadLocalVariables>& tl, int idn) : id(idn), tlv(tl) {} void run() { while(!tlv->isCanceled()) { tlv->increment(); cout << *this << endl; } } friend ostream& operator<<(ostream& os, Accessor& a) { return os << "#" << a.id << ": " << a.tlv->get(); } }; int main() { cout << "Press <Enter> to quit" << endl; try { CountedPtr<ThreadLocalVariables> tlv(new ThreadLocalVariables); const int SZ = 5; ThreadedExecutor executor; for(int i = 0; i < SZ; i++) executor.execute(new Accessor(tlv, i)); cin.get(); tlv->cancel(); // All Accessors will quit } catch(Synchronization_Exception& e) { cerr << e.what() << endl; } } ///:~
Listado 10.23. C11/ThreadLocalVariables.cpp
Cuando crea un objeto ThreadLocal
instanciando la plantilla, únicamente puede acceder al
contenido del objeto utilizando los métodos set() y get(). El
método get() devuelve una copia del objeto que está asociado a
ese hilo, y set() inserta su argumento dentro del objeto
almacenado para ese hilo, devolviendo el objeto antiguo que se
encontraba almacenado. Puede comprobar que esto se utiliza en
increment() y get()
de ThreadLocalVariables
.
Ya que tlv se comparte en múltiples
objetos Accessor
, está escrito como
un Cancelable
, por lo que
los Accessors
puede recibir señales
cuando queramos parar el sistema.
Cuando ejecute este programa se evidenciará que se reserva para cada hilo su propio almacenamiento.