A task is a lightweight object for managing parallel work units. It avoids starting special threads by using the CLR thread pool, and can more efficiently use the thread pool. List of task-related classes in the system. Threading. Tasks namespace:
| Class |
Function |
| Task |
Manage Work Units |
| Task <tresult> |
Manage work units with return values |
| Taskfactory |
Create a task |
| Taskfactory <tresult> |
Create a task or a continuation task with the same return value |
| Taskscheduler |
Manage Task Scheduling |
| Taskcompletionsource |
Manual control of task Workflow |
Tasks are used to execute tasks in parallel and make full use of multiple cores: in fact, parallel and Plinq are built on the parallel structure of tasks.
Tasks provide a series of powerful features to manage work units, including:
- Coordinate Task Scheduling
- Create a parent-child relationship for a task to start from another task
- Cooperative cancellation Mode
- No-signal task waiting
- Attach a continuation task (continuation)
- Schedules a continuation Task Based on Multiple ancestor tasks
- Passing an exception to the parent task, continuation task, or task consumer
At the same time, a task implements a local work queue, which allows you to efficiently create fast-executed sub-tasks without the cost of competing in a single work queue. The task parallel library allows you to create hundreds of tasks at minimal cost. However, if you want to create millions of tasks, you must split these tasks into larger units to maintain efficiency.
Create and start a task
You can create a task by using the startnew () method of taskfactory. You can also call the task constructor to create a task and start the task manually. Note that the task is not executed immediately after it is started. It is managed by the taskscheduler.
- Example of creating a task using the startnew () method of taskfactory:
// No Return Value
Task. Factory. startnew () => console. writeline ("task created! "));
// Return Value
VaR task = task. Factory. startnew <string> () => "task created! ");
Console. writeline (task. Result );
- An example of manually starting a start method is as follows:
VaR task = new task <string> () => "task created! ");
Task. Start (); // asynchronous execution
Console. writeline (task. Result );
- An example of manually starting a runsynchronously call is as follows:
VaR task = new task <string> () => "task created! ");
Task. runsynchronously (); // synchronous execution
Console. writeline (task. Result );
You can also specify a task status parameter when creating a task. You can access this parameter through the asyncstate attribute of the task. Example:
var task = Task.Factory.StartNew(state => "hello " + state, "Mike");
Console.WriteLine(task.AsyncState);
Console.WriteLine(task.Result);
You can also specify taskcreationoptions, which has the following enumerated values: None, longrunning, preferfairness, and attachedtoparent. The following describes the functions of enumeration values.
- Longrunning: as its name implies, it is a long-running task. It is recommended that the Task Scheduler allocate a dedicated thread to the task. The reason for this is that long running tasks may block the task queue, resulting in short tasks not being executed. Longrunning is also suitable for blocking tasks.
- Preferfairness: Fair first, this option suggests that the task scheduler schedule tasks based on the task start time as much as possible. However, it may not do this because it uses local work to steal queues (local work-stealing queues) to optimize task scheduling. This optimization is useful for very small tasks.
- Attachtoparent: attach to parent task. This option is used to create a subtask. Example of creating a subtask:
Method 1:
VaR parent = task. Factory. startnew () =>
{
VaR nonchildtask = task. Factory. startnew (
() => Console. writeline ("I'm not a child task .")
);
VaR childtask = task. Factory. startnew (
() => Console. writeline ("I'm a child task ."),
Taskcreationoptions. attachedtoparent );
});
Method 2:
Task parent = new task () =>
{
Dostep1 ();
});
Task task2 = parent. continuewith (prevtask) =>
{
Dostep2 ();
});
Parent. Start ();
Task waiting
You can use the wait () member method or the result attribute to wait for the task to complete.
When the result attribute is called, the following operations are performed:
- If the task has ended, return the task result
- If the task has started, wait until the task ends
- If the task has not started execution, the task will be executed in the current thread
The task. waitany () Static Method waits for any task to complete. Example:
VaR tasks = new task [3];
For (INT I = 0; I <tasks. length; I ++)
{
Int taskindex = I;
Tasks [I] = task. Factory. startnew () =>
{
Int seed = guid. newguid (). gethashcode ();
Int waittime = new random (SEED). Next (10,100 );
Thread. Sleep (waittime );
Console. writeline ("task {0} finished", taskindex );
});
}
Task. waitany (tasks );
Console. writeline ("completed tasks ");
The task. waitall () Static Method waits for all tasks to be completed. Even if a task throws an exception, it does not terminate the wait. After all tasks are completed, it throws an aggresponexception. This exception aggregates the exceptions thrown by all tasks. Example:
VaR tasks = new task [3];
For (INT I = 0; I <tasks. length; I ++)
{
Int taskindex = I;
Tasks [I] = task. Factory. startnew () =>
{
Int waittime = new random (guid. newguid (). gethashcode (). Next (10,100 );
Thread. Sleep (waittime );
Console. writeline ("task {0} finished", taskindex );
});
}
Task. waitall (tasks );
Console. writeline ("all tasks completed ");
Exception Handling
By default, applications are terminated if the task is not handled. It should be noted that unhandled exceptions in the task will not immediately lead to application termination. The exception will be terminated only when the Garbage Collector recycles the task and calls the Finalize method. If the exception attribute of the task is read, this operation will prevent subsequent application termination.
When the task is completed, all unhandled exceptions are passed to the caller.
An exception that occurs when the wait () method times out must also be handled; otherwise, the application is terminated.
Unhandled exceptions in the subtask are passed to the parent task by bubbling; exceptions in non-subtasks in the nested task are not passed to the previous Task of the subtask, which must be handled independently, otherwise, the application will be terminated.
var task = Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
});
task.Wait();
Taskschedexception. unobservedtaskexception static events provide the last method to handle all unprocessed exceptions. By handling this event, you do not need to terminate the application, but replace it with your own exception handling logic.
Cancel task
When creating a task, you can pass in a cancelationtoken parameter to cancel the task safely. Example:
var source = new CancellationTokenSource();
var token = source.Token;
var task = Task.Factory.StartNew(() =>
{
Console.WriteLine("Task starting...");
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine("I'm alive. {0}",DateTime.Now);
Thread.Sleep(1000);
}
},token);
Task.Factory.StartNew(() =>
{
Thread.Sleep(4500);
source.Cancel();
});
try
{
task.Wait();
Console.WriteLine("Task stopped.");
}
catch (AggregateException e)
{
if (e.InnerException is OperationCanceledException)
{
Console.WriteLine("Task canceled.");
}
else
{
Console.WriteLine("errors.");
}
}
If you cancel a task by calling the cancellationtokensource cancel () method, the task will not be terminated immediately until the next time the task is detected to be canceled, operationcanceledexception will be thrown to terminate the task.
If you want to cancel a task by directly throwing an operationcanceledexception exception, you must input the cancelationtoken parameter in the task. Otherwise, the task cannot be in the taskstatus. Canceled state and the onlyoncanceled continuation task is triggered.
In addition, the cancel token can also be passed to the wait and cancelandwait methods to cancel the wait.