Maison  >  Article  >  développement back-end  >  Analyse des points d'utilisation abusive dans la programmation multithread .Net

Analyse des points d'utilisation abusive dans la programmation multithread .Net

怪我咯
怪我咯original
2017-04-10 10:59:311896parcourir

Cet article présente principalement l'analyse des points d'utilisation abusive dans la programmation multithread .Net. Il a une certaine valeur de référence, jetons un coup d'oeil avec l'éditeur ci-dessous

1 Problème de variable partagée

Mauvaise écriture :

Toutes les tâches peuvent partager la même variable, donc les résultats de sortie peuvent être les mêmes.

public static void Error()
{
   for(int i=0;i<10;i++)
   {
    Task.Run(() => { Console.WriteLine("{0}", i); });
   }
}

Écriture correcte :

Attribuer la variable i à la variable locale temp , afin que chaque tâche utilise une valeur i différente.

public static void Right()
{
   for (int i = 0; i < 10; i++)
   {
    int temp = i;
    Task.Run(() => { Console.WriteLine("{0}", temp); });
   }
}

2 Ne pas nettoyer les ressources requises pour les tâches en attente

Mauvaise écriture :

Sortez du contenu texte de manière asynchrone, donc avant d'utiliser StreamReader, la variable sr a quitté sa portée et la méthode Dispose est appelée.

public static void Error()
{
   using (StreamReader sr = new StreamReader(@"D:\说明.txt", Encoding.Default))
   {
    Task.Run(() => { Console.WriteLine("输出:{0}",sr.ReadLine()); });
   }
}

Écriture correcte :

public static void Right()
{
   using (StreamReader sr = new StreamReader(@"D:\说明.txt", Encoding.Default))
   {
    var task = Task.Run(() => { Console.WriteLine("输出:{0}", sr.ReadLine()); });
    task.Wait();
   }
}

3 Évitez de verrouiller ceci, typeof(type), string

La bonne approche : définissez un champ privé en lecture seule de type objet et verrouillez-le.

4 Le nombre de waitHandles dans WaitHandle.WaitAll doit être inférieur ou égal à 64

public static void Error()
{
   ManualResetEvent[] manualEvents = new ManualResetEvent[65];

   try
   {
    for (int i = 0; i < 65; i++)
    {
     var temp = i;
     Task.Run(() =>
     {
      manualEvents[temp] = new ManualResetEvent(false);
      Console.WriteLine("{0}", temp);
      manualEvents[temp].Set();
     });
    }
    WaitHandle.WaitAll(manualEvents);
   }
   catch (Exception ae)
   {
    Console.WriteLine(ae.Message);
   }
}

5 Impossible d'intercepter l'exception

try
{
    var task = Task.Run(() => { throw new Exception("抛异常"); });
    //如果将下面这行代码注掉,则无法抛出异常
    task.Wait();
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

6 Les ressources de la tâche doivent-elles être libérées

Il est recommandé d'appeler Dispose, mais ne pas appeler n'est pas une erreur grave.

Notez que les ressources ne sont pas autorisées à être libérées lorsque la tâche est dans certains états, sinon une erreur sera signalée.

public static void CatchException()
{
   try
   {
    Console.WriteLine("开始");
    var task = Task.Run(() =>
    {
     //throw new Exception("抛异常"); 
    });
    //注掉下面这行代码,观察异常结果
    //task.Wait();
    task.Dispose();
    Console.WriteLine("结束");
   }
   catch(Exception ex)
   {
    Console.WriteLine(ex.Message);
   }
}

7 Démonstration de blocage

Supposons tsak1 et task2 si le premier verrou est obtenu avec succès avant l'obtention du deuxième verrou (pour tsak1, le deuxième verrou demandé est LockedObj2, et pour la tâche2, il s'agit de LockedObj1), un blocage se produira.

private static readonly Object LockedObj1 = new object();
private static readonly Object LockedObj2 = new object();
public static void LockShow()
{
   var task1 = Task.Run(() => 
   {
    lock (LockedObj1)
    {
     Console.WriteLine("get LockedObj1");
     lock (LockedObj2)
     {
      Console.WriteLine("get LockedObj2....");
     }
    }
   });
   var task2 = Task.Run(() =>
   {
    lock (LockedObj2)
    {
     Console.WriteLine("get LockedObj2");
     lock (LockedObj1)
     {
      Console.WriteLine("get LockedObj1....");
     }
    }
   });
}

Plusieurs exécutions peuvent produire les deux résultats suivants : la première image est une situation dans laquelle une impasse ne se produit pas, et la deuxième image est lorsqu'une impasse se produit.

8 N'appelez pas la méthode Thread.Abort.

Task ne fournit pas la méthode Abort. Si vous utilisez le nouveau TPL (après .NET 4.0), vous ne penserez pas à ce problème. CancellationToken est généralement utilisé pour contrôler l'annulation des tâches.

9 Assurez-vous que les variables partagées sont sûres

Exécutez à plusieurs reprises, vous pouvez observer différents résultats, comme le montre la figure ci-dessous.

public static void Func()
{
   string s = "ASDFGH";
   Parallel.Invoke(
    () => { s = s.Replace("A", "1"); s = s.Replace("S", "1s"); }, 
    () => { s = s.Replace("A", "2"); s = s.Replace("S", "2s"); }, 
    () => { s = s.Replace("A", "3"); });
   Console.WriteLine(s);
}

10 surabonnement et sous-application du processeur

public static void Func()
{
   ParallelOptions po = new ParallelOptions();
   //超额申请,处理器只有4个逻辑内核,结果设置并行度为10且是个逻辑内核均在工作,等待的任务数量大于0.
   po.MaxDegreeOfParallelism = 10;
   //申请不足,处理器有4个逻辑内核,却指定并行度为3,还有一个空闲的内核没有被占用(也有可能被其他线程占用,这里假设在指定并行度为3的情况下,另一个内核空闲)
   po.MaxDegreeOfParallelism = 3;
   List<int> list = new List<int>();
   Parallel.ForEach(list, po, m =>
   {
    //业务
   });
}

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn