Outils pour utilisateurs

Outils du site


developpement:dotnet:csharp:introduction

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
developpement:dotnet:csharp:introduction [2023/10/04 02:10] – [Signaling] sgariepydeveloppement:dotnet:csharp:introduction [2023/10/06 05:06] (Version actuelle) – [Reactive] sgariepy
Ligne 1148: Ligne 1148:
  
  
 +===== Manual Event Reset =====
  
  
 +<code csharp>
 +static ManualResetEvent customEvent = new ManualResetEvent(false);
 +// Equivalent: static EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
  
 +void Main()
 +{
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    
 +    Thread.Sleep(1000);
 +    Console.WriteLine("Press a key to release all the threads");
 +    Console.Read();
 +    
 +    customEvent.Set();
 +    
 +    Thread.Sleep(1000);
 +    Console.WriteLine("Press a key again.  Thread won't block even if they call WaitOne");
 +    Console.Read();
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Thread.Sleep(1000);
 +
 +    Console.WriteLine("Press a key again.  Thread will block if they call WaitOne");
 +    Console.Read();
 +    customEvent.Reset();
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Task.Factory.StartNew(CallWaitOne);
 +    Thread.Sleep(1000);
 +
 +    Console.WriteLine("Press a key again calls Set()");
 +    Console.Read();
 +    customEvent.Set();
 +    
 +    Console.ReadLine();
 +}
 +
 +void CallWaitOne()
 +{
 +    Console.WriteLine("{0} has called WaitOne", Task.CurrentId);
 +    customEvent.WaitOne();
 +    Console.WriteLine("{0} finally ended", Task.CurrentId);
 +}
 +</code>
 +
 +
 +===== Countdown =====
 +
 +<code csharp>
 +static CountdownEvent customCountdown = new CountdownEvent(5);
 +
 +void Main()
 +{
 +    Task.Factory.StartNew(DoSomething);
 +    Task.Factory.StartNew(DoSomething);
 +    Task.Factory.StartNew(DoSomething);
 +    Task.Factory.StartNew(DoSomething);
 +    Task.Factory.StartNew(DoSomething);
 +    
 +    customCountdown.Wait();
 +    Console.WriteLine("Signal has been called 5 times");
 +}
 +
 +void DoSomething()
 +{
 +    Thread.Sleep(250);
 +    Console.WriteLine("{0} is calling signal", Task.CurrentId);
 +    customCountdown.Signal();
 +}
 +</code>
 +
 +====== Task Parallel Library (TPL) ======
 +
 +
 +  * Set of public types and APIs that can be found in two namespaces:
 +    * System.Threading
 +    * System.Threading.Tasks
 +  * Simplifies process of adding parallelism and concurrency to applications
 +  * Value is ability to scale degree of concurrency dynamically
 +  * Handles partitioning of work
 +  * Schedules threads on ThreadPool
 +  * Allows for task cancellation
 +  * Handles state management
 +  * Not all code is suitable for parallelization
 +  * Threading of any type has an associated overhead
 +  * In some cases, multithreading may be slower than sequential code
 +
 +Exemple simple:
 +
 +<code csharp>
 +void Main()
 +{
 +    Stopwatch stopwatch = new Stopwatch();
 +    stopwatch.Start();
 +    for (int i = 0; i < 10; i++)
 +    {
 +        Console.WriteLine(i);
 +    }
 +    stopwatch.Stop();
 +    Console.WriteLine("Time taken: {0}", stopwatch.ElapsedTicks);
 +    stopwatch.Start();
 +    
 +    Parallel.For(0, 10, i => {
 +        Console.WriteLine(i);
 +    });
 +
 +    stopwatch.Stop();
 +    Console.WriteLine("Time taken: {0}", stopwatch.ElapsedTicks);
 +}
 +</code>
 +
 +Le cas en parallèle prend plus de temps dû au overhead.  L'instruction n'étant pas assez complexe pour justifier la parallèlisation.
 +
 +
 +<code csharp>
 +// Générer des images: https://picsum.photos/3840/2160
 +
 +void Main()
 +{
 +    var path = Directory.GetCurrentDirectory();
 +    var files = Directory.GetFiles(path + @"\pictures", "*.jpg");
 +    
 +    var normalAlteredPath = path + @"\normalAlteredPath";
 +    var parallelAlteredPath = path + @"\parallelAlteredPath";
 +    Directory.CreateDirectory(normalAlteredPath);
 +    Directory.CreateDirectory(parallelAlteredPath);
 +    
 +    ParallelExecutionMode(files, normalAlteredPath);
 +    NormalExecutionMode(files, parallelAlteredPath);
 +}
 +
 +void NormalExecutionMode(string[] files, string alteredPath)
 +{
 +    Stopwatch stopwatch = Stopwatch.StartNew();
 +    foreach (var currentFile in files)
 +    {
 +        var file = Path.GetFileName(currentFile);
 +        using (var fileBitmap = new Bitmap(currentFile))
 +        {
 +            fileBitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
 +            fileBitmap.Save(Path.Combine(alteredPath, file));
 +            Console.WriteLine("Thread {0}", Thread.CurrentThread.ManagedThreadId);
 +        }
 +    }
 +    Console.WriteLine("Normal execution time: {0}", stopwatch.ElapsedMilliseconds);
 +    stopwatch.Stop();
 +}
 +
 +void ParallelExecutionMode(string[] files, string alteredPath)
 +{
 +    Stopwatch stopwatch = Stopwatch.StartNew();
 +
 +    Parallel.ForEach(files, currentFile => {
 +        var file = Path.GetFileName(currentFile);
 +        using (var fileBitmap = new Bitmap(currentFile))
 +        {
 +            fileBitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
 +            fileBitmap.Save(Path.Combine(alteredPath, file));
 +            Console.WriteLine("Thread {0}", Thread.CurrentThread.ManagedThreadId);
 +        }
 +    });
 +
 +    Console.WriteLine("Parallel execution time: {0}", stopwatch.ElapsedMilliseconds);
 +    stopwatch.Stop();
 +}
 +</code>
 +
 +
 +
 +<code csharp>
 +void Main()
 +{
 +    var list = Enumerable.Range(0, 100000000).ToArray();
 +    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
 +    
 +    ParallelOptions parallelOptions = new ParallelOptions();
 +    parallelOptions.CancellationToken = cancellationTokenSource.Token;
 +    parallelOptions.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
 +    
 +    Console.WriteLine("Press 'x' to cancel");
 +    
 +    Task.Factory.StartNew(() =>
 +    {
 +        if (Console.ReadLine() == "x")
 +        {
 +            cancellationTokenSource.Cancel();
 +        }
 +    
 +        long total = 0;
 +        
 +        try
 +        {
 +            Parallel.For<long>(0, list.Length, parallelOptions, () => 0, (count, parallelLoopState, subtotal) =>
 +            {
 +                Thread.Sleep(200);
 +                parallelOptions.CancellationToken.ThrowIfCancellationRequested();
 +                subtotal += list[count];
 +                return subtotal;
 +            },
 +            (x) =>
 +            {
 +                Interlocked.Add(ref total, x);
 +            });
 +        }
 +        catch (OperationCanceledException ex)
 +        {
 +            Console.WriteLine("Cancelled " + ex.Message);
 +        }
 +        finally
 +        {
 +            cancellationTokenSource.Dispose();
 +        }
 +        Console.WriteLine("The final sum is {0}", total);
 +    });
 +    
 +}
 +</code>
 +
 +===== Continuation with state =====
 +
 +<code csharp>
 +void Main()
 +{
 +    Task<DateTime> task = Task.Run(() => DoSomething());
 +    List<Task<DateTime>> continuationTasks = new List<Task<DateTime>>();
 +    
 +    for (int i = 0; i < 3; i++)
 +    {
 +        task = task.ContinueWith((x, y) => DoSomething(), new Person { Id = i });
 +        continuationTasks.Add(task);
 +    }
 +    
 +    task.Wait();
 +    
 +    foreach (var continuation in continuationTasks)
 +    {
 +        Person person = continuation.AsyncState as Person;
 +        Console.WriteLine("Task finished at " + continuation.Result + ". Person id is {0}", person.Id);
 +    }
 +}
 +
 +static DateTime DoSomething()
 +{
 +    return DateTime.Now;
 +}
 +
 +internal class Person
 +{
 +    public int Id { get; set; }
 +}
 +</code>
 +
 +===== TaskCompletionSource =====
 +
 +<code csharp>
 +void Main()
 +{
 +    TaskCompletionSource<Product> taskCompletionSource = new TaskCompletionSource<Product>();
 +    Task<Product> lazyTask = taskCompletionSource.Task;
 +    
 +    Task.Factory.StartNew(() => {
 +        Thread.Sleep(2000);
 +        taskCompletionSource.SetResult(new Product { Id = 1, Name = "Some name" });
 +    });
 +    
 +    Task.Factory.StartNew(() =>
 +    {
 +        if (Console.ReadLine() == "x")
 +        {
 +            Product result = lazyTask.Result;
 +            Console.WriteLine("Result is {0}", result.Name);
 +        }
 +    });
 +    
 +    Thread.Sleep(5000);
 +}
 +
 +class Product
 +{
 +    public int Id { get; set; }
 +    public string Name { get; set; }
 +}
 +
 +</code>
 +
 +===== PLINQ =====
 +
 +Parallel LINQ:
 +
 +  * Automates parallelization
 +  * Considéré déclaratif plutôt qu'impératif
 +  * Opérateurs qui font en sorte que ce n'est pas parallélisé:
 +    * Take, Select, SelectMany, Skip, TakeWhile, SkipWhile, ElementAt
 +  * Anomalies
 +    * Join, GroupBy, GroupJoin, Distinct, Union, Intersect, Except
 +  * Force parallelism:
 +    * .AsParallel().withExecutionMode(ParallelExecution.ForceParallelism)
 +
 +
 +<code csharp>
 +void Main()
 +{
 +    var list = Enumerable.Range(1, 100000);
 +    var primeNumbers = list
 +                        .AsParallel()
 +                        .Where(IsPrime);
 +    Console.WriteLine("{0} prime numbers", primeNumbers.Count());
 +}
 +
 +bool IsPrime(int x)
 +{
 +    if (x == 1) return false;
 +    if (x == 2) return true;
 +    if (x % 2 == 0) return false;
 +    var boundary = (int)Math.Floor(Math.Sqrt(x));
 +
 +    for (int i = 3; i <= boundary; i += 2)
 +    {
 +        if (x % i == 0)
 +        {
 +            return false;
 +        }
 +    }
 +    return true;    
 +}
 +</code>
 +
 +==== Degree of Parallelism ====
 +
 +<code csharp>
 +void Main()
 +{
 +    List<string> websites = new List<string>();
 +    websites.Add("apple.com");
 +    websites.Add("google.com");
 +    websites.Add("microsoft.com");
 +    
 +    List<PingReply> responses = websites
 +                                    .AsParallel()
 +                                    .WithDegreeOfParallelism(websites.Count())
 +                                    .Select(PingSites)
 +                                    .ToList();
 +    
 +    foreach (var response in responses)
 +    {
 +        Console.WriteLine(response.Address + " " + response.Status + " " + response.RoundtripTime);
 +    }
 +    
 +    Console.ReadLine();
 +}
 +
 +private static PingReply PingSites(string websiteName)
 +{
 +    Ping ping = new Ping();
 +    return ping.Send(websiteName);
 +}
 +</code>
 +
 +
 +
 +
 +====== Thread Marshalling ======
 +
 +  * [[http://bigballofmud.wordpress.com/2009/03/21/thread-marshalling-part-1-creating-a-thread-in-windows-forms/|Thread Marshalling]]
 +  * [[http://www.albahari.com/threading/|Threading (Joseph Albahari)]]
 +
 +
 +====== Pattern Matching ======
 +
 +<code csharp>
 +void Main()
 +{
 +    var circle = new Circle(5);
 +    var circleRadius100 = new Circle(250);
 +    var rectangle = new Rectangle(420, 1337);
 +    var square = new Rectangle(70, 70);
 +
 +    var shapes = new List<Shape> { circle, circleRadius100, rectangle, square };
 +
 +    var randomShape = shapes[new Random().Next(shapes.Count)];
 +
 +    CSharp6Feature(randomShape);
 +    CSharp7Feature(randomShape);
 +    CSharp8Feature(randomShape);
 +    CSharp9Feature(randomShape);
 +}
 +
 +private void CSharp6Feature(Shape shape)
 +{
 +    Console.WriteLine("=== C# 6 Pattern matching features ===");
 +    if (shape is Circle) // 'is' operator
 +    {
 +        var circle = (Circle)shape;
 +        Console.WriteLine($"Circle with radius {circle.Radius}");
 +    }
 +    else
 +    {
 +        Console.WriteLine($"Shape is something else");
 +    }
 +}
 +
 +private void CSharp7Feature(Shape shape)
 +{
 +    Console.WriteLine("=== C# 7 Pattern matching features ===");
 +
 +    if (shape is Circle circle)  // implicit casting
 +    {
 +        Console.WriteLine($"Circle with radius {circle.Radius}");
 +    }
 +    else
 +    {
 +        Console.WriteLine($"Shape is something else");
 +    }
 +    
 +    // using switch
 +    
 +    switch (shape)
 +    {
 +        case Circle c:
 +            Console.WriteLine($"Circle with radius {c.Radius}");
 +            break;
 +        case Rectangle r when r.Height == r.Width:
 +            Console.WriteLine($"This is a square");
 +            break;
 +        default:
 +            Console.WriteLine($"Shape is something else");
 +            break;
 +    }
 +}
 +
 +private void CSharp8Feature(Shape shape)
 +{
 +    Console.WriteLine("=== C# 8 Pattern matching features ===");
 +
 +    if (shape is Circle { Radius: 10 })
 +    {
 +        Console.WriteLine($"Circle with radius of 10");
 +    }
 +    
 +    var shapeDetails = shape switch
 +    {
 +        Circle => "This is a circle", // we can drop the 'cir' is not used
 +        Rectangle rec when rec.Height == rec.Width => "This is a square",
 +        _ => "Shape is something else"
 +    };
 +}
 +
 +private void CSharp9Feature(Shape shape)
 +{
 +    Console.WriteLine("=== C# 9 Pattern matching features ===");
 +
 +    if (shape is not Rectangle) // 'not' added
 +    {
 +        Console.WriteLine($"This is not a rectangle");
 +    }
 +
 +    if (shape is Circle { Radius: > 100 and < 200, Area: >= 1000 })
 +    {
 +        Console.WriteLine($"Circle with radius greater than 100");
 +    }
 +
 +
 +    // that can be used like so: if (shape is not null) {...}
 +
 +    var shapeDetails = shape switch
 +    {
 +        Circle => "This is a circle", // we can drop the 'cir' is not used
 +        Rectangle rec when rec.Height == rec.Width => "This is a square",
 +        { Area: 100 } => "Area is 100",
 +        _ => "Shape is something else"
 +    };
 +
 +    var areaDetails = shape.Area switch
 +    {
 +        >= 100 and <= 200 => "Area is between 100 and 200",
 +        _ => ""
 +    };
 +    
 +}
 +
 +public static class Extensions
 +{
 +    public static bool IsLetter(this char c) =>
 +        c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
 +}
 +
 +public abstract class Shape
 +{
 +    public abstract double Area { get; }
 +}
 +
 +public class Rectangle : Shape, ISquare
 +{
 +    public Rectangle(int height, int width)
 +    {
 +        Height = height;
 +        Width = width;
 +    }
 +    
 +    public override double Area => Height * Width;
 +    
 +    public int Height { get; set; }
 +    public int Width { get; set; }
 +}
 +
 +public class Circle : Shape
 +{
 +    private const double PI = Math.PI;
 +    
 +    
 +    public Circle(int diameter)
 +    {
 +        Diameter = diameter;
 +    }
 +
 +    public int Diameter { get; set; }
 +    public int Radius => Diameter / 2;
 +
 +    public override double Area => PI * Radius * Radius;
 +}
 +
 +public interface ISquare
 +{
 +    int Height { get; set; }
 +    int Width { get; set; }
 +}
 +
 +static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
 +    => (groupSize, visitDate.DayOfWeek) switch
 +    {
 +        (<= 0, _) => throw new ArgumentException("Group size must be positive"),
 +        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
 +        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
 +        (>= 10, DayOfWeek.Monday) => 30.0m,
 +        (>= 5 and < 10, _) => 12.0m,
 +        (>= 10, _) => 15.0m,
 +        _ => 0.0m
 +    };
 +
 +</code>
  
  
Ligne 1194: Ligne 1742:
  
  
-====== Thread Marshalling ====== 
  
-  * [[http://bigballofmud.wordpress.com/2009/03/21/thread-marshalling-part-1-creating-a-thread-in-windows-forms/|Thread Marshalling]] 
-  * [[http://www.albahari.com/threading/|Threading (Joseph Albahari)]] 
  
  
developpement/dotnet/csharp/introduction.1696378258.txt.gz · Dernière modification : 2023/10/04 02:10 de sgariepy