Cet article présente principalement les connaissances pertinentes sur la synchronisation des threads en C#. Il a une très bonne valeur de référence, jetons-y un œil avec l'éditeur ci-dessous
Avant-propos
Lorsqu'un thread du pool de threads est bloqué, le pool de threads créera des threads supplémentaires, et la création, la destruction et la planification de threads nécessitent des ressources mémoire assez coûteuses. De plus, de nombreux développeurs ont l'habitude de créer plus de threads lorsqu'ils voient que les threads de leurs programmes ne font rien d'utile pour créer des threads évolutifs et évolutifs. programmes réactifs, nous avons présenté l'explication détaillée de la programmation asynchrone C# avant
Cependant, il existe également de sérieux problèmes dans la programmation asynchrone si deux threads différents accèdent aux mêmes variables et données, selon l'implémentation de notre fonction asynchrone, non Il peut y avoir deux threads accédant aux mêmes données en même temps. Dans ce cas, nous avons besoin d'une synchronisation des threads. Lorsque plusieurs threads accèdent aux données partagées en même temps, la synchronisation des threads peut empêcher la corruption des données. La raison pour laquelle le concept de simultanéité est mis en avant est que l'essence de la synchronisation des threads est une question de timing.
Asynchrone et synchrone sont relatifs. La synchronisation signifie une exécution séquentielle. Après en avoir exécuté une, vous devez attendre et coordonner l'exécution de la suivante. Asynchrone signifie qu'ils sont indépendants les uns des autres et continuent de faire leurs propres choses en attendant un événement. Il n'est pas nécessaire d'attendre la fin de l'événement avant de travailler à nouveau. Les threads sont un moyen de devenir asynchrone. Asynchrone signifie que le thread principal qui appelle la méthode n'a pas besoin d'attendre la fin d'un autre thread de manière synchrone, afin que le thread principal puisse faire autre chose.
Constructions primitives en mode utilisateur et en mode noyau
Concepts de base
Primitives : Oui Constructions simples utilisées dans le code
Mode utilisateur : en coordonnant les threads via des instructions spéciales du processeur, le système d'exploitation ne détecte jamais un blocage de thread sur une construction primitive en mode utilisateur.
Mode Kernel : Fournies par Windows lui-même, les fonctions implémentées par le noyau sont appelées dans le thread de l'application.
Constructions en mode utilisateur
Constructions volatiles
Le compilateur C#, le compilateur JIT et les processeurs optimiser le code, et ils essaient de s'assurer que nos intentions sont conservées. Cependant, d'un point de vue multi-thread, nos intentions peuvent ne pas être conservées :
static void Main(string[] args) { Console.WriteLine("让worker函数运行5s后停止"); var t = new Thread(Worker); t.Start(); Thread.Sleep(5000); stop = true; Console.ReadLine(); } private static bool stop = false; private static void Worker(object obj) { int x = 0; while (!stop) { x++; } Console.WriteLine("worker函数停止x={0}",x); }
class test { private static int m_flag = 0; private static int m_value = 0; public static void Thread1(object obj) { m_value = 5; m_flag = 1; } public static void Thread2(object obj) { if (m_flag == 1) Console.WriteLine("m_value = {0}", m_value); } //多核CPU机器才会出现线程同步问题 public void Exec() { var thread1 = new Thread(Thread1); var thread2 = new Thread(Thread2); thread1.Start(); thread2.Start(); Console.ReadLine(); } }
Construction de verrouillage
Chaque méthode de la classe System.Threading.Interlocked effectue une opération de lecture et d'écriture atomique avant d'appeler une méthode Interlocked. Toutes les écritures de variables sont effectuées avant cet appel à la méthode Interlocked, et toutes les lectures de variables après l'appel sont effectuées après cet appel. La méthode Interlocked effectue principalement des opérations statiques sur les variables INT32 telles que Add, Decrement, Compare, Exchange, CompareChange et d'autres méthodes, et accepte également les paramètres d'objet, Double et d'autres types. Opération atomique : fait référence à une opération qui ne sera pas interrompue par le mécanisme de planification des threads ; une fois cette opération démarrée, elle s'exécutera jusqu'à la fin sans aucun changement de contexte (passage à un autre thread) au milieu. Démonstration de code : Description : interrogez de manière asynchrone plusieurs serveurs Web via la méthode Interlocked et renvoyez des données en même temps, et les résultats ne sont exécutés qu'une seule fois.//上报状态类型 enum CoordinationStatus { Cancel, Timeout, AllDone }
class AsyncCoordinator { //AllBegun 内部调用JustEnded来递减它 private int _mOpCount = 1; //0=false,1=true private int _mStatusReported = 0; private Action<CoordinationStatus> _mCallback; private Timer _mTimer; //发起一个操作之前调用 public void AboutToBegin(int opsToAdd = 1) { Interlocked.Add(ref _mOpCount, opsToAdd); } //处理好一个操作的结果之后调用 public void JustEnded() { if (Interlocked.Decrement(ref _mOpCount) == 0) { ReportStatus(CoordinationStatus.AllDone); } } //该方法必须在发起所有操作后调用 public void AllBegin(Action<CoordinationStatus> callback, int timeout = Timeout.Infinite) { _mCallback = callback; if (timeout != Timeout.Infinite) { _mTimer = new Timer(TimeExpired, null, timeout, Timeout.Infinite); JustEnded(); } } private void TimeExpired(object o) { ReportStatus(CoordinationStatus.Timeout); } public void Cancel() { ReportStatus(CoordinationStatus.Cancel); } private void ReportStatus(CoordinationStatus status) { //如果状态从未报告过,就报告它,否则就忽略它,只调用一次 if (Interlocked.Exchange(ref _mStatusReported, 1) == 0) { _mCallback(status); } } }
class MultiWebRequest { //辅助类 用于协调所有的异步操作 private AsyncCoordinator _mac = new AsyncCoordinator(); protected Dictionary<string,object> _mServers = new Dictionary<string, object> { {"http://www.baidu.com",null},{"http://www.Microsoft.com",null},{"http://www.cctv.com",null}, {"http://www.souhu.com",null},{"http://www.sina.com",null},{"http://www.tencent.com",null}, {"http://www.youku.com",null} }; private Stopwatch sp; public MultiWebRequest(int timeout = Timeout.Infinite) { sp = new Stopwatch(); sp.Start(); //通过异步方式一次性发起请求 var httpclient = new HttpClient(); foreach (var server in _mServers.Keys) { _mac.AboutToBegin(1); httpclient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task)); } _mac.AllBegin(AllDone,timeout); Console.WriteLine(""); } private void ComputeResult(string server, Task<Byte[]> task) { object result; if (task.Exception != null) { result = task.Exception.InnerException; } else { //线程池处理IO result = task.Result.Length; } //保存返回结果的长度 _mServers[server] = result; _mac.JustEnded(); } public void Cancel() { _mac.Cancel(); } private void AllDone(CoordinationStatus status) { sp.Stop(); Console.WriteLine("响应耗时总计{0}",sp.Elapsed); switch (status) { case CoordinationStatus.Cancel: Console.WriteLine("操作取消"); break; case CoordinationStatus.AllDone: Console.WriteLine("操作完成,完成的结果如下"); foreach (var server in _mServers) { Console.WriteLine("{0}",server.Key); object result = server.Value; if (result is Exception) { Console.WriteLine("错误原因{0}",result.GetType().Name); } else { Console.WriteLine("返回字节数为:{0}",result); } } break; case CoordinationStatus.Timeout: Console.WriteLine("操作超时"); break; default: throw new ArgumentOutOfRangeException("status", status, null); } } }
Verrouillage rotatif simple
class SomeResource { private SimpleSpinLock s1 = new SimpleSpinLock(); public void AccessResource() { s1.Enter(); //一次是有一个线程才能进入访问 s1.Leave(); } } class SimpleSpinLock { private int _mResourceInUse; public void Enter() { while (true) { if(Interlocked.Exchange(ref _mResourceInUse,1)==0) return; } } public void Leave() { Volatile.Write(ref _mResourceInUse,1); } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!