Version C # du mot-clé synchronisé de Java?

313

C # a-t-il sa propre version du mot-clé "synchronisé" java?

C'est-à-dire en java, il peut être spécifié soit pour une fonction, un objet ou un bloc de code, comme ceci:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}

ou

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
la source
3
La forme de bloc nécessite une référence pour verrouiller. Dans la forme de la méthode, l'objet verrou est implicitement ceci (ou la classe [this.class, pas getClass ()] pour les méthodes statiques, mais ne verrouille pas les classes).
Tom Hawtin - tackline
1
Toujours pas protégé? Je viens toujours ici parce que je ne me souviens pas de cette [MethodImpl(MethodImplOptions.Synchronized)]ligne.
Bitterblue
1
Je pense que votre 2e extrait ne compilerait pas - il doit être synchronisé sur quelque chose.
PoweredByRice

Réponses:

466

Premièrement - la plupart des classes n'auront jamais besoin d'être thread-safe. Utilisez YAGNI : n'appliquez la sécurité des threads que lorsque vous savez que vous allez réellement l'utiliser (et le tester).

Pour les choses au niveau de la méthode, il y a [MethodImpl]:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {/* code */}

Cela peut également être utilisé sur les accesseurs (propriétés et événements):

private int i;
public int SomeProperty
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return i; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { i = value; }
}

Notez que les événements de type champ sont synchronisés par défaut, tandis que les propriétés implémentées automatiquement ne le sont pas :

public int SomeProperty {get;set;} // not synchronized
public event EventHandler SomeEvent; // synchronized

Personnellement, je n'aime pas l'implémentation MethodImplcar elle se verrouille thisou typeof(Foo)- ce qui est contraire aux meilleures pratiques. L'option préférée consiste à utiliser vos propres verrous:

private readonly object syncLock = new object();
public void SomeMethod() {
    lock(syncLock) { /* code */ }
}

Notez que pour les événements de type champ, l'implémentation de verrouillage dépend du compilateur; dans les anciens compilateurs Microsoft, il s'agit d'un lock(this)/ lock(Type)- cependant, dans les compilateurs plus récents, il utilise desInterlocked mises à jour - donc thread-safe sans les parties désagréables.

Cela permet une utilisation plus granulaire et permet l'utilisation de Monitor.Wait/ Monitor.Pulseetc pour communiquer entre les threads.

Une entrée de blog connexe ( revisitée plus tard ).

Marc Gravell
la source
@earcam et votre question est? Cette affirmation est vraie. La grande majorité des classes n'ont aucune exigence de sécurité des threads, leur sécurité de thread ne sera pas testée et leur sécurité affectera les performances. Le nombre de types qui ont vraiment besoin de se soucier des threads est très faible - collections intentionnellement synchronisées, multiplexeurs, etc.
Marc Gravell
4
Je pense que j'aurais dû simplement dire; "la plupart des classes n'auront jamais besoin d'être thread-safe" mais "tous les développeurs doivent être conscients de la concurrence". Rétrospectivement, je conviens que le nombre est très petit (et certainement quelque chose que vous voulez obtenir une fois au même endroit, permettant à la majorité des classes d'interagir sans se soucier de leur environnement multithread). J'aimerais avoir supprimé le commentaire plus rapidement =)
earcam
6
Le billet de blog lié de Marc a un suivi en mars 2010, disant que dans .NET 4.0, MethodImplet les événements de type champ génèrent désormais un bon code de synchronisation, et il n'est plus nécessaire d'utiliser vos propres verrous.
Rory O'Kane
2
De nos jours, une bonne majorité des applications sont basées sur le Web, servies avec des cadres reposant sur une réutilisation intensive des instances et un cycle de vie d'objet complexe via l'injection de dépendances. La mentalité par défaut de nos jours a tendance à se tromper du côté de la sécurité des threads.
Sheepy
1
@Elazar trop tard maintenant, mais pour mémoire: changer le framework est une modification d'une ligne vers leur csproj si vous l'avez créé en utilisant les modèles de base .net standard / .net - et le .net régulier est disponible, tout comme le multi -cible. Cependant, l'outillage IDE autour de cela est tout simplement terrible - il vous suffit de savoir ce que vous pouvez changer et à quoi :)
Marc Gravell
56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Jan Gressmann
la source
10
Voulez-vous vraiment déclarer votre objet verrou comme statique ..?
serg10
21
Bien sûr, pour que chaque thread puisse y accéder facilement sans transmettre de références.
Jan Gressmann
33
Si nous sommes dans le contexte de la question du demandeur, alors nous parlons de méthodes d'instance. L'utilisation de statique signifie que si le thread 1 appelle instance1.DoSomething () et le thread 2 appelle instance2.DoSomething, le deuxième appel se bloquera même s'il s'agit d'un objet complètement différent. L'appel de thread2 ne doit pas bloquer sauf si quelqu'un appelle DoSomething sur le même objet . Ne pas dire que vous vous trompez, mais dire qu'il est important de comprendre l'effet de l'utilisation statique ici, car cela peut entraîner de mauvaises performances en bloquant globalement plutôt que par instance.
AaronLS
1
@AaronLS Le verrou statique est très utile lorsque votre objet effectue des actions sur une portée plus grande que lui-même. Cela arrive toujours avec les services web par exemple.
Thibault D.
4
-1 car il s'agit d'un comportement différent de celui demandé par l'OP. Il s'agit d'un verrou de classe, pas d'un verrou d'instance.
tster
39

C # a-t-il sa propre version du mot-clé "synchronisé" java?

Non. En C #, vous avez explicitement des lockressources sur lesquelles vous souhaitez travailler de manière synchrone sur des threads asynchrones. lockouvre un bloc; cela ne fonctionne pas au niveau de la méthode.

Cependant, le mécanisme sous-jacent est similaire car lockfonctionne en invoquant Monitor.Enter(et par la suite Monitor.Exit) sur le runtime. Java fonctionne de la même façon, selon la documentation Sun .

Konrad Rudolph
la source
3
Il n'a pas de "mot-clé" équivalent, mais comme le montre la réponse de Marc Gravell ci-dessus, vous pouvez synchroniser au niveau de la méthode en utilisant l'annotation [MethodImpl (MethodImplOptions.Synchronized)].
MindJuice
1
Puisque la synchronizedméthode on de Java n'est fondamentalement synchronized (this.getClass())pas la même chose en C # lock(typeof(this))?
Sri Harsha Chilakapati
2
@SriHarshaChilakapati qui n'est que partiellement correct, le synchronizedmot - clé de java sur une méthode ressemble plus à :, synchronized(this)seulement sur une méthode statique, il se comporte comme synchronized(class).
bvdb
6

Prenez note, avec des chemins complets la ligne: [MethodImpl(MethodImplOptions.Synchronized)]devrait ressembler

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

Traubenfuchs
la source
1
ou vous pouvez simplement utiliserusing System.Runtime.CompilerServices;
aloisdg se déplaçant vers codidact.com
J'ai écrit ce commentaire alors que je ne savais pas encore comment insérer automatiquement des instructions using, après avoir programmé C # pendant pas plus de quelques jours ou semaines et je suis étonné de ces 3 votes positifs.
Traubenfuchs
1
Vous avez aidé au moins 3 développeurs et c'est bien :)
aloisdg se déplaçant sur codidact.com
5

Vous pouvez utiliser l' lockinstruction à la place. Je pense que cela ne peut que remplacer la deuxième version. Rappelez - vous aussi que les deux synchronizedet ont lockbesoin pour fonctionner sur un objet.

James
la source