2016-07-12 3 views
0

J'ai un fichier avec un chemin de base pour toutes mes ressources. Par exemple:Le code produira-t-il une condition de concurrence?

build/scripts/script1.js 
build/scripts/script2.js 

Je bien sûr besoin d'un chemin de base par exemple:

https://example.org/SuperDuperSite/build/scripts/script1.js 

Ce que j'espérais faire était de charger le fichier dans un dictionnaire global au démarrage du chemin. Le dictionnaire devrait seulement être chargé une fois. Malheureusement, autant que je sache, le chemin de base n'est pas disponible avant la première requête. Donc, dans asp.net je dois utiliser application_beginrequest plutôt que application_start. Ce qui est mauvais à ce sujet est maintenant que je dois faire face à des problèmes de multithreading.

qui me force à écrire le type de code suivant:

lock(_lock) { 
    if (_dictionary == null) { 
     LoadDictionary(); 
    } 
    } 

Ce serait appelé à chaque demande quand je ne ai vraiment besoin de charger une fois. Je n'aime vraiment pas ça bien sûr. Je ne veux pas avoir à verrouiller toutes les demandes pour des raisons de performance. Une solution, après avoir parlé aux collèges que nous sommes arrivés avec:

if (_dictionary == null) 
{ 
    lock(_lock) { 
     if (_dictionary == null) { 
     LoadDictionary(); 
     } 
    } 
} 

Donc, avec cette solution, je ne serais pas obligé de verrouiller toutes les demandes, mais si plusieurs threads ont fini par obtenir cette section au démarrage je puis protéger en vérifiant si l'objet est à nouveau nul dans le verrou. Ce code fonctionnera-t-il ou est-ce que je vais rencontrer un problème de concurrence?

+0

Semble beaucoup de complexité pour inclure une URL de base. Y at-il une raison pour laquelle vous avez pris le chemin de le charger dans un dictionnaire au démarrage? –

+1

Etes-vous sûr de l'attaquer du bon angle? Y a-t-il une raison pour laquelle vous ne pouvez pas faire de demandes de chemin relatif? –

+1

Le double verrouillage est sûr dans C#.Les conditions dans l'échantillon de code sont en arrière, mais d'autres fins. –

Répondre

1

Soyez prudent lorsque vous utilisez verrous à double contrôle.

Oui, il est thread-safe, mais dans votre code particulier vous croiserez peut-être un bug subtil où _dictionary a été instancié par l'autre fil (en passant le contrôle null), mais pas complètement peuplé encore , et vous pouvez finir par essayer d'accéder à un dictionnaire partiellement peuplé. Et finir avec l'un de ces deux:

  1. vous manque des résultats lors de la lecture du dictionnaire, ou pire
  2. Sauf si vous utilisez un ConcurrentDictionary (qui vient aussi avec ses implications sur les performances), vous pouvez être lire et écrire simultanément dans le dictionnaire, et provoquer un blocage (oui, la classe Dictionary est connue pour causer des blocages dans de nombreux codes).

Un drapeau bool _dictionaryLoaded (revérifié), à true retourné à la fin de LoadDictionary(), est probablement mieux.

Ou utilisez Lazy<> si vous êtes sur .NET 4. C'est beaucoup plus propre, tout ce que vous avez à faire est de passer en LoadDictionary pour être utilisé comme la fonction init.

Édition: Lazy <> est internally implemented with a double-checked lock.