workers threads thread qué por multiple los ejemplo crearon concurrent chrome javascript multithreading local-storage

javascript - threads - web workers ejemplo



¿Es seguro el hilo localStorage? (1)

Sí, es hilo seguro. Sin embargo, su código no es atómico y ese es su problema allí. localStorage a la seguridad de localStorage de localStorage pero primero, cómo solucionar su problema.

Ambas pestañas pueden pasar el cheque if juntos y escribir en el elemento sobrescribiéndose entre sí. La forma correcta de manejar este problema es usar StorageEvent s.

Estos le permiten notificar a otras ventanas cuando una clave ha cambiado en localStorage, resolviendo efectivamente el problema para usted en un mensaje integrado que pasa de manera segura. Aquí hay una buena lectura sobre ellos . Vamos a dar un ejemplo:

// tab 1 localStorage.setItem("Foo","Bar"); // tab 2 window.addEventListener("storage",function(e){ alert("StorageChanged!"); // this will run when the localStorage is changed });

Ahora, lo que prometí sobre la seguridad del hilo :)

Como me gusta, observemos esto desde dos ángulos, desde la especificación y el uso de la implementación.

La especificación

Vamos a mostrar que es seguro para hilos por especificación.

Si verificamos la especificación de almacenamiento web , podemos ver que nota específicamente :

Debido al uso de la exclusión mutua de almacenamiento, múltiples contextos de navegación podrán acceder a las áreas de almacenamiento local simultáneamente de tal manera que los scripts no puedan detectar ninguna ejecución de script concurrente.

Por lo tanto, el atributo de longitud de un objeto de almacenamiento y el valor de las distintas propiedades de ese objeto no pueden cambiar mientras se ejecuta un script, de otra manera que no sea predecible por el propio script.

Incluso elabora aún más:

Siempre que las propiedades de un localStorage de Storage del atributo localStorage se examinen, devuelvan, configuren o eliminen, ya sea como parte de un acceso directo a la propiedad, al verificar la presencia de una propiedad, durante la enumeración de propiedades, al determinar el número de propiedades presentes , o como parte de la ejecución de cualquiera de los métodos o atributos definidos en la interfaz de almacenamiento, el agente de usuario primero debe obtener la exclusión mutua de almacenamiento .

Énfasis mío. También señala que a algunos implementadores no les gusta esto como una nota.

En la práctica

Vamos a mostrar que es seguro para subprocesos en la implementación.

Al elegir un navegador aleatorio, elegí WebKit (porque no sabía dónde se encuentra ese código antes). Si verificamos en la implementación de Storage de WebKit, podemos ver que tiene su cuota de exclusión mutua.

Vamos a tomarlo desde el principio. Cuando llamas a setItem o assign, esto sucede:

void Storage::setItem(const String& key, const String& value, ExceptionCode& ec) { if (!m_storageArea->canAccessStorage(m_frame)) { ec = SECURITY_ERR; return; } if (isDisabledByPrivateBrowsing()) { ec = QUOTA_EXCEEDED_ERR; return; } bool quotaException = false; m_storageArea->setItem(m_frame, key, value, quotaException); if (quotaException) ec = QUOTA_EXCEEDED_ERR; }

A continuación, esto sucede en StorageArea :

void StorageAreaImpl::setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException) { ASSERT(!m_isShutdown); ASSERT(!value.isNull()); blockUntilImportComplete(); String oldValue; RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue, quotaException); if (newMap) m_storageMap = newMap.release(); if (quotaException) return; if (oldValue == value) return; if (m_storageAreaSync) m_storageAreaSync->scheduleItemForSync(key, value); dispatchStorageEvent(key, oldValue, value, sourceFrame); }

Tenga en cuenta que blockUntilImportComplete aquí. Veamos eso:

void StorageAreaSync::blockUntilImportComplete() { ASSERT(isMainThread()); // Fast path. We set m_storageArea to 0 only after m_importComplete being true. if (!m_storageArea) return; MutexLocker locker(m_importLock); while (!m_importComplete) m_importCondition.wait(m_importLock); m_storageArea = 0; }

También fueron tan lejos como para agregar una buena nota:

// FIXME: In the future, we should allow use of StorageAreas while it''s importing (when safe to do so). // Blocking everything until the import is complete is by far the simplest and safest thing to do, but // there is certainly room for safe optimization: Key/length will never be able to make use of such an // optimization (since the order of iteration can change as items are being added). Get can return any // item currently in the map. Get/remove can work whether or not it''s in the map, but we''ll need a list // of items the import should not overwrite. Clear can also work, but it''ll need to kill the import // job first.

Explicar esto funciona, pero puede ser más eficiente.

Tengo curiosidad acerca de la posibilidad de dañar la entrada de localStorage sobrescribiéndola en dos pestañas del navegador simultáneamente. ¿Debo crear un mutex para el almacenamiento local?
Ya estaba pensando en tal pseudo-clase:

LocalStorageMan.prototype.v = LocalStorageMan.prototype.value = function(name, val) { //Set inner value this.data[name] = val; //Delay any changes if the local storage is being changed if(localStorage[this.name+"__mutex"]==1) { setTimeout(function() {this.v(name, val);}, 1); return null; //Very good point @Lightness Races in Orbit } //Lock the mutext to prevent overwriting localStorage[this.name+"__mutex"] = 1; //Save serialized data localStorage[this.name] = this.serializeData; //Allow usage from another tabs localStorage[this.name+"__mutex"] = 0; }

La función anterior implica que el administrador de almacenamiento local está administrando una clave específica del almacenamiento local, por localStorage["test"] , localStorage["test"] . Quiero usar esto para los scripts de greasomonkey, donde evitar los conflictos es una prioridad.