Task uses detailed [base operation, asynchronous principle, asynchronous function, asynchronous mode]

Task is FrameWork4.0 was introduced, FrameWork4.5 Some additional features have been added, such as Task.Run(), async/await keywords, etc.

In. NET FrameWork4.5. Task-based Asynchronous Pattern (TAP) has become the mainstream mode for task-based asynchronous processing since then.

Before using the asynchronous function, look at the basic operations of Task.

1. Task Basic Operations

1.1 Task Startup Mode

Task.Run(()=>Console.WriteLine("Hello Task"));
Task.Factory.StartNew(()=>Console.WriteLine("Hello Task"));

Task.Run is Task. Factory. Shortcut to StartNew.

Starts with background threads and defaults to threads from the thread pool

Task.Run(() =>
{
    Console.WriteLine(
        $"TaskRun IsBackGround:{CurrentThread.IsBackground}, IsThreadPool:{CurrentThread.IsThreadPoolThread}");
});

Task.Factory.StartNew(() =>
{
    Console.WriteLine(
        $"TaskFactoryStartNew IsBackGround:{CurrentThread.IsBackground}, IsThreadPool:{CurrentThread.IsThreadPoolThread}");
});

If Task is a long task, you can add TaskCreationOptions. The LongRunning parameter prevents the task from running on the thread pool, which improves performance.

Task.Factory.StartNew(() =>
{
    Console.WriteLine(
        $"TaskFactoryStartNew IsBackGround:{CurrentThread.IsBackground}, IsThreadPool:{CurrentThread.IsThreadPoolThread}");
}, TaskCreationOptions.LongRunning);

1.2 Task return value/with parameters

Task has a generic subclass, Task<TResult>, that allows a value to be returned.

Task<string> task =Task.Run(()=>SayHello("Jack"));

string SayHello(string name)
{
    return "Hello " + name;
}

Console.WriteLine(task.Result);

Get the return value from the task's Result property, which can clog threads, especially in desktop client programs, using Task with caution. Result, deadlock prone!

It is also not reasonable to have parameters, which can be directly replaced by async/await.

1.3 Task exception/exception handling

When code in a task throws an unhandled exception, the exception is re-thrown when the task's Wait() or Result property is called.

var task = Task.Run(ThrowError);
try
{
    task.Wait();
}
catch(AggregateException ex)
{
    Console.WriteLine(ex.InnerException is NullReferenceException ? "Null Error!" : "Other Error");
}


void ThrowError()
{
    throw new NullReferenceException();
}

For autonomous tasks (no wait() and Result or continuous tasks), use the static event TaskScheduler.UnobservedTaskException can subscribe to unobserved exceptions globally.

To log errors

1.4 Task Continuation

Continuation is usually implemented by a callback method, which executes after the task is completed. There are two methods of continuation

(1) Calling the task's GetAwaiter method returns a awaiter object. The OnCompleted method of this object tells the task to call a delegate when execution is complete or an error occurs.

Task<string> learnTask = Task.Run(Learn);
var awaiter = learnTask.GetAwaiter();
awaiter.OnCompleted(() =>
{
    var result = awaiter.GetResult();
    Console.WriteLine(result);
});

string Learn()
{
    Console.WriteLine("Learn Method Executing");
    Thread.Sleep(1000);
    return "Learn End";
}

If the learnTask task fails, continue the code awaiter.GetResult() will re-throw the exception, where GetResult can get the original exception directly, and if the Result property method is used, only AggergateException can be resolved.

This continuation method is more applicable to rich client programs, and continuations can be submitted to the synchronization context and continue back to the UI thread.

When writing library files, you can use the ConfigureAwait method to continue the code for a while on the thread where the task runs, avoiding unnecessary switching overhead.

var awaiter =learnTask.ConfigureAwait(false).GetAwaiter();

(2) Another way to use ContiuneWith

Task<string> learnTask = Task.Run(Learn);
learnTask.ContinueWith(antecedent =>
{
    var result = learnTask.Result;
    Console.WriteLine(result);
});

string Learn()
{
    Console.WriteLine("Learn Method Executing");
    Thread.Sleep(1000);
    return "Learn End";
}

When a task has errors, you must handle AggregateException, and ContiuneWithis better suited for parallel programming scenarios.

1.5 TaskCompletionSource class use

You can see from the source code below that when you instantiate TaskCompletionSource, the constructor creates a new Task task.

public class TaskCompletionSource
{
  private readonly Task _task;
  
  /// <summary>Creates a <see cref="TaskCompletionSource"/>.</summary>
  public TaskCompletionSource() => _task = new Task();
  
  /// <summary>
        /// Gets the <see cref="Tasks.Task"/> created
        /// by this <see cref="TaskCompletionSource"/>.
        /// </summary>
        /// <remarks>
        /// This property enables a consumer access to the <see cref="Task"/> that is controlled by this instance.
        /// The <see cref="SetResult"/>, <see cref="SetException(Exception)"/>, <see cref="SetException(IEnumerable{Exception})"/>,
        /// and <see cref="SetCanceled"/> methods (and their "Try" variants) on this instance all result in the relevant state
        /// transitions on this underlying Task.
        /// </remarks>
  public Task Task => _task;
}

Its real purpose is to create a task that does not bind a thread.

eg: You can use the Timer class, and the CLR triggers an event after the timer without using a thread.

Implement the generic Delay method:

Delay(5000).GetAwaiter().OnCompleted(()=>{ Console.WriteLine("Delay End"); });

Task Delay(int millisecond)
{
    var tcs = new TaskCompletionSource<object>();
    var timer = new System.Timers.Timer(millisecond) { AutoReset = false };
    timer.Elapsed += delegate
    {
        timer.Dispose();
        tcs.SetResult(null);
    };
    timer.Start();
    return tcs.Task;
}

This method is similar to Task.Delay() method.

 

To be continued.

Keywords: C#

Added by ozzy on Wed, 05 Jan 2022 03:10:31 +0200