[WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7

This post is available in english.

En d’autres mots, il n’est pas supporté !

Et le pire est que l’on est même pas averti que ce n’est pas supporté... Le code compile, mais l'attribut n'a aucun effet ! On peut bien entendu lire l’article “the differences between silverlight on Windows and Windows Phone”, mais bon, il est facile de l’oublier. Peut-être qu’une règle d’analyse statique de code pourrait empêcher cela.

Mais enfin, vous voudrez probablement utiliser ThreadStatic parce que vous en avez besoin. Mais comme ce n’est pas supporté, vous pourriez aller vers Thread.GetNamedDataSlot, vous me direz.

Pas de chance. Ce n’est pas supporté non plus.

Cela nous laisse à l’implémentation de notre propre TLS, à la main.

 

Mise à jour de Umbrella pour Silverlight sur Windows Phone

Je suis un grand fan d’Umbrella, et la première fois que j’ai eu à utiliser Dictionary<>.TryGetValue et son magique paramètre “out”, dans l'essai de portage de mon application de Controle à Distance, j’ai décidé de porter Umbrella vers Windows Phone 7. Pour enfin utiliser GetValueOrDefault sans le réécrire, encore.

J’ai réussi à faire passer la majorité des tests unitaires, excepté ceux qui émettent du code, utilisent des fonctionnalités du web, utilisent les sérialiseurs xml et binaires, appellent des méthodes privées via la réflection, et ainsi de suite.

Il y a quelques autres parties qui nécessitaient d’être mise à jour, parceque la classe TypeDescriptor n’est pas disponible en WP7, et il faut passer par un try/catch pour vérifier qu’une valeur est convertible d’un type vers un autre. Mais ce n’est pas vraiment gênant, et cela fonctionne comme attendu.

 

ThreadLocalSource dans Umbrella

Umbrella contient une classe nommée ThreadLocalSource qui encapsule le comportement du TLS, et il est très facile de créer une variable statique de ce type, plutôt que d’utiliser une variable statique ThreadStatic.

Les exemples Quick Start en font ce genre d’utilisation :

1
2
3
4
5
6
7
8
9
10
    ISource<int> threadLocal = new ThreadLocalSource<int>(1);

int valueOnOtherThread = 0;

Thread thread = new Thread(() => valueOnOtherThread = threadLocal.Value);
thread.Start();
thread.Join();

Assert.Equal(1, threadLocal.Value);
Assert.Equal(0, valueOnOtherThread);

La Thread principale place la valeur à 1, et l’autre thread essaye de lire la même variable et elle doit être différente. (La valeur par défaut d’un int, qui est 0).

 

Mise à jour de ThreadLocalSource pour éviter l’utilisation de ThreadStatic

L’implémentation du TLS dans .NET est principalement un dictionnaire de paires de string/object qui est attaché à chaque thread active. Donc pour mimiquer cela, on a simplement besoin de créer une liste de toutes les threads qui ont besoin de stocker quelque chose pour elles-mêmes, et l’encapsuler proprement.

On peut créer une variable comme celle-ci :

1
    private static Tuple<WeakReference, IDictionary<string, T>>[] _tls;

Cette variable est intentionnellement du type d’un tableau pour tenter d’utiliser la localité spatiale en mémoire. Puisque sur cette plateforme il ne devrait pas y avoir beaucoup de threads, il est tout à fait acceptable de parcourir le tableau pour en trouver une. Cette approche tente d’être sans locks, en utilisant un mécanisme de “retry” pour mettre à jour le tableau. Le type WeakReference est utilisé pour éviter de garder une référence vers les threads une fois qu’elles sont terminées.

Donc, pour faire la mise à jour du tableau, on peut s’y prendre comme suit :

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
    private static IDictionary<string, T> GetValuesForThread(Thread thread)
{
// Find the TLS for the specified thread
var query = from entry in _tls

// Only get threads that are still alive
let t = entry.T.Target as Thread

// Get the requested thread
where t != null && t == thread
select entry.U;

var localStorage = query.FirstOrDefault();

if (localStorage == null)
{
bool success = false;

// The storage for the new Thread
localStorage = new Dictionary<string, T>();

while(!success)
{
// store the original array so we can check later if there has not
// been anyone that has updated the array at the same time we did
var originalTls = _tls;

var newTls = new List<Tuple<WeakReference, IDictionary<string, T>>>();

// Add the slots for which threads references are still alive
newTls.AddRange(_tls.Where(t => t.T.IsAlive));

var newSlot = new Tuple<WeakReference, IDictionary<string, T>>()
{
T = new WeakReference(thread),
U = localStorage
};

newTls.Add(newSlot);

// If no other thread has changed the array, replace it.
success = Interlocked.CompareExchange(ref _tls, newTls.ToArray(), originalTls) != _tls;
}
}

return localStorage;
}


L’utilisation d’une approche sans lock comme celle-ci devrait limiter la contention autour de l’utilisation de cette classe de simili-TLS. Il pourrait y avoir, de temps à autres, des manipulations faites plusieurs fois lors de “race conditions” sur la mise à jour de la variable _tls, mais cela est tout à fait acceptable. De plus, les livelocks ne peuvent pas arriver, considérant le type de système préemptif sur lequel WP7 fonctionne.

J’ai l’impression que développer sur cette plateforme va nécessiter plein de petits “hacks” du genre... Ca va être intéressant !

Publié dimanche 20 juin 2010 00:11 par jay
Classé sous , , , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: [WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 11:32

Pas mal comme blague.

Du coup pour ce qui est des alertes sur les dépendances, NDepend peut aider un peu :

//

<Name>Check for ThreadStaticAttribute usage</Name>

<Name>Check for Thread.GetNamedDataSlot usage</Name>

coq

# re: [WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 11:37

Super, le commentaire a été purgé de toute la partie utile :-D

coq

# re: [WP7Dev] Attention à l’attribut [ThreadStatic] dans Silverlight pour Windows Phone 7 @ dimanche 20 juin 2010 15:08

En effet, NDepend peut tout à fait faire l'affaire, sauf pour Thread.GetNamedDataSlot, qui n'est tout simplement pas présent... Cela m'a fait comprendre que le début de mon post n'était pas assez précis. ThreadStatic est bien présent, mais il n'a aucun effet !

jay


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01