2010-08-18 4 views
4

Environnement: .NET 3.5 SP1..NET: Comment s'assurer que Thread 1 peut voir ce que Thread 2 a écrit dans un champ?

J'ai deux threads: un thread d'interface utilisateur et un thread de travail d'arrière-plan. Le thread de travail d'arrière-plan met périodiquement à jour certains champs dans un objet partagé et le thread d'interface utilisateur les vérifie. Rien de spectaculaire - juste la progression, les valeurs de retour et les exceptions levées. Le thread de travail soulève également certains événements sur le thread d'interface utilisateur (via Control.BeginInvoke) lorsqu'il modifie ces champs.

Le thread de travail écrit WRITES ces champs, et le thread UI les lisent SEULEMENT. Ils ne sont pas utilisés pour d'autres communications. Par souci de performance, je voudrais éviter le verrouillage sur l'objet partagé ou les propriétés individuelles. Il n'y aura jamais d'état non valide dans l'objet partagé.

Cependant, je suis préoccupé par des choses comme les caches de processeurs et les optimisations de compilateurs. Comment puis-je éviter la situation lorsqu'une valeur mise à jour n'est pas visible dans le gestionnaire d'événements sur le thread d'interface utilisateur? Est-ce que l'ajout de volatile à tous les champs sera suffisant?

+1

Évitez d'utiliser des objets partagés. C'est une mauvaise pratique en général. Utilisez simplement des événements ou des messages pour communiquer entre les threads. – Peladao

+0

Pouvez-vous donner un exemple de la façon dont un cache de processeur ou une optimisation de compilateur peut provoquer l'échec de votre programme? Je ne comprends pas ce que vous voulez dire. –

+0

Cookies @Rice Flour - Eh bien, si le thread de travail a terminé la tâche et a écrit quelque chose dans le champ "Résultat", mais le thread UI voit encore là pour les 5 prochaines heures, il ne serait pas très productif ... –

Répondre

0

Il est de loin préférable d'utiliser des directives multithreading établies. Producer/consumer collections sont disponibles dans .NET 4.0; Ce sont également disponible pour .NET 3.5 si vous incluez des références au Rx library.

Le code sans verrouillage est presque impossible à obtenir. Si vous ne voulez pas utiliser la file d'attente producteur/consommateur, utilisez un verrou.

Si vous insistez pour suivre le chemin de la douleur, alors oui, volatile permettra à n'importe quel thread de lire cette variable pour obtenir la dernière valeur écrite.

+0

Eh bien, tant qu'un seul thread est en train d'écrire et l'autre en train de lire - qu'est-ce qui peut mal tourner? : P –

+0

Savez-vous que [l'idiome de verrouillage à double vérification est rompu] (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)? Si cela est obscur, n'écrivez pas de code sans verrouillage. –

+0

Ne pas être pointilleux ou quoi que ce soit, mais n'est pas cet article pour Java? –

0

En général, je déconseille d'utiliser des mécanismes de synchronisation avancés, car ils sont notoirement difficiles à obtenir, mais dans ce cas, un appel à Thread.MemoryBarrier pourrait être acceptable. Bien sûr, cela suppose qu'il n'y a pas d'exigences d'atomicité et que l'objet partagé ne peut jamais être dans un état semi-cuit. Cela peut être plus facile que de tout saupoudrer avec volatile. Peut-être que concevoir le code de sorte que l'objet partagé soit immuable serait préférable. Ensuite, tout ce que vous avez à faire est d'échanger la référence de l'objet partagé en une opération atomique simple et rapide.

volatile object shared; 

void WorkerThread() 
{ 
    // The following operation is safe because the variable is volatile. 
    shared = GetNewSharedObject(); 
} 

void UIThread() 
{ 
    // The following operation is safe because the variable is volatile. 
    object value = shared.SomeProperty; 
} 
+0

Il n'y a pas beaucoup de champs, donc saupoudrer de 'volatile 'est tout à fait acceptable. Et comment cela peut-il être compliqué si un fil écrit seulement et l'autre seulement? –

+0

Vous serez probablement d'accord pour marquer tout comme «volatile» alors. Et vous avez raison, un lecteur et un auteur simplifient beaucoup ** les choses. –

1

Tout va bien, pas besoin de s'inquiéter. Une barrière de mémoire est requise pour vider toutes les écritures en attente en mémoire. Il y a un implicite avec n'importe quelle instruction lock. Control.Begin/Invoke() doit prendre un verrou pour protéger la liste des délégués en attente, ce qui est suffisant.

L'exigence volatile est plus difficile, principalement parce que sa sémantique exacte est si mal documentée. Sur le matériel x86/x64, il empêche simplement le compilateur JIT de mettre en cache la valeur d'une variable dans un registre CPU. Ce n'est pas un problème dans votre cas car la cible du délégué pointe vers une méthode. Les variables ne sont pas mises en cache dans les méthodes si les méthodes ne sont pas en ligne. Votre cible de délégué ne peut pas être insérée.

Questions connexes