Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Task in C#

Tech May 14 1

Task in C#

  1. Task represents the execution and completion of an asynchronous operation. It can be used to encapsulate an asynchronous operation, allowing it to execute without blocking the main thread and retrieve the result after completion.
static void Main(string[] args)
{
    MyFun();
    Console.Read();
}

static void MyFun()
{
    // var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
    Task t1 = new Task<int>(() => MyMethod("write"));
    Task t2 = Task<int>.Factory.StartNew(() => MyMethod("red")); // This starts automatically
    t1.Start(); // This must be started manually
}

static int MyMethod(string name)
{
    Console.WriteLine($"I am {name}");
    return 0;
}
  1. Example: UseMethodAsync() submits the operation to the thread pool via Task.Run and uses await to wait for completion and retrieve the result.
static async Task<int> LongOperationAsync()
{    
    // Simulate a time-consuming operation    
    await Task.Delay(1000);    
    return 42; // Assume this is the result of the long operation
}

static async Task UseMethodAsync()
{    
    int result = await Task.Run(() => LongOperationAsync());    
    Console.WriteLine($"Operation result: {result}");
}
  1. Parallel tasks with Task.WhenAll: Process multiple tasks in parallel and wait for all to complete before proceeding.
static async Task ParallelProcessingAsync()
{    
    Task<int> task1 = Task.Run(() => LongOperationAsync());    
    Task<int> task2 = Task.Run(() => LongOperationAsync());    
    Task<int> task3 = Task.Run(() => LongOperationAsync());
    
    // Wait for all tasks to complete    
    await Task.WhenAll(task1, task2, task3);
    
    // Continue processing results    
    Console.WriteLine("All tasks completed");
}
  1. Cancellation in Tasks
static async Task<int> LongOperationWithCancellationAsync(CancellationToken cancellationToken)
{    
    await Task.Delay(1000, cancellationToken);    
    return 42;
}

static async Task MyMethodWithCancellationAsync()
{    
    using (var cts = new CancellationTokenSource())    
    {        
        Task<int> task = Task.Run(() => LongOperationWithCancellationAsync(cts.Token));                
        // Simulate timeout logic; in practice, other conditions may trigger        
        cts.CancelAfter(1000); // Cancel after 1000ms (1 second)
        
        try        
        {           
            int result = await task; 
            Console.WriteLine($"Operation result: {result}");        
        }        
        catch (OperationCanceledException)        
        {            
            Console.WriteLine("Operation was canceled");        
        }    
    }
}
  1. Error handling in Tasks
static async Task<int> OperationWithFailAsync()
{   
    // Simulate an operation that may fail    
    await Task.Delay(100);    
    throw new InvalidOperationException("Operation failed");
}

static async Task HandleTaskAsync()
{   
    try   
    {       
        await Task.Run(() => OperationWithFailAsync());    
    }    
    catch (Exception ex)   
    {        
        Console.WriteLine($"Error occurred: {ex.Message}");    
    }
}
  1. Continuation with Task.ContinueWith: Allows specifying another task to run after the first task completes.

    a. ContinueWith() waits until the first task's status reaches IsCompleted because TaskContinuationOptions.OnlyOnRanToCompletion is used.

static int TaskMethod(string name, int seconds)
{
    Console.WriteLine("Task {0} is running. Thread id {1}, Is thread pool: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    Thread.Sleep(TimeSpan.FromSeconds(seconds));
    return 60 * seconds;
}

static void Main(string[] args)
{
    var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
    firstTask.ContinueWith(t => Console.WriteLine("First Task Result: {0}.. Thread id {1}, Is thread pool: {2}", t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread), TaskContinuationOptions.OnlyOnRanToCompletion);
    firstTask.Start();

    // Main thread work
    Console.WriteLine("Back Work: Thread id {0}, Is thread pool: {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    Console.Read();
}

Note: The main thread is not blocked.

b. Example with two tasks and continuation options.

static int TaskMethod(string name, int seconds)
{
    Console.WriteLine("Task {0} is running. Thread id {1}, Is thread pool: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
    Thread.Sleep(TimeSpan.FromSeconds(seconds));
    return 60 * seconds;
}

static void Main(string[] args)
{
    Console.WriteLine("Main thread id {0}, Is thread pool: {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);

    var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
    var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));

    firstTask.ContinueWith(t => Console.WriteLine("First Task Result: {0}.. Thread id {1}, Is thread pool: {2}", t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread), TaskContinuationOptions.OnlyOnRanToCompletion);

    firstTask.Start();
    secondTask.Start();

    // Comment out the next line to see different behavior:
    // Thread.Sleep(TimeSpan.FromSeconds(4)); // Wait for firstTask, secondTask and continuations to complete.
    
    secondTask.ContinueWith(t => Console.WriteLine("Second Task Result: {0}.. Thread id {1}, Is thread pool: {2}", t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);

    // Main thread work
    Console.Read();
}

If the Thread.Sleep is comented out, the main thread ends early, and secondTask's continuation runs on the thread pool. If uncommented, the main thread sleeps for 4 seconds, allowing both tasks to complete, and due to ExecuteSynchronously, the continuation of secondTask runs on the main thread.

  1. Task.WhenAny and Task.WhenAll
static async Task<string> DoMethod(int time, string name = "wo")
{
    Console.WriteLine($"{name} come in.");
    await Task.Delay(TimeSpan.FromMilliseconds(time));
    return string.Format("{0} have done this work, time is {1}", name, time);
}

static async Task TimeOutMethod(int needTimeOut)
{
    Console.WriteLine($"needTimeOut={needTimeOut}");
    var t = new Stopwatch();
    t.Start(); // Start timing
    var timeOutTask = Task.Run(() => Task.Delay(150).ContinueWith(_ => "Task timeOutTask finished after 150 seconds")); // Create a timeout task.
    var doing = DoMethod(needTimeOut); // The task to execute.       
    var task = await Task.WhenAny(timeOutTask, doing); // Return as soon as either completes
    Console.WriteLine(task.Result);   
    t.Stop();
    Console.WriteLine("used time: " + t.Elapsed.TotalMilliseconds);
}

static void Main(string[] args)
{
    TimeOutMethod(50);
    TimeOutMethod(500);
    Console.Read();
}

Explanation: With parameter 50, DoMethod completes first, returning "wo have done this work, time is 50". With parameter 500, timeOutTask completes first, returning "Task timeOutTask finished after 150 seconds".

/// <summary>
/// Execute multiple tasks together, collect results, and process them.
/// </summary>
static async Task Tasks()
{
    var t = new Stopwatch();
    t.Start(); // Start timing
    var list = new List<string>();
    var t1 = DoMethod(1000, "1000");
    var t2 = DoMethod(1500, "1500");
    var t3 = DoMethod(1200, "1200");
    var tasks = new[] { t1, t2, t3 };

    // Collect results
    var process = tasks.Select(async tt =>
    {
        var result = await tt;
        list.Add(result);
    });
    // Wait for all tasks to complete before using results
    await Task.WhenAll(process);
    foreach (var i in list)
    {
        Console.WriteLine(i);
    }
    t.Stop();
    Console.WriteLine("used time: " + t.Elapsed.TotalMilliseconds);
}

static void Main(string[] args)
{
    Tasks();
    Console.Read();
}

This waits for all tasks (the shorter ones finish earlier but results are collected after all complete).

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.