任务(Task)表示完成的某个工作单元。这个工作单元可以在单独的线程中运行,也可以以同步的方式启动一个任务,这需要等待主调线程。使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。
具体场景:在安排需要完成的工作时,任务提供了非常强大的灵活性。如,可以定义一个连续的工作——在一个任务完成后该执行什么工作。这可以任务成功与否来区分。另外,还可以在层次结构中安排任务。例如,父任务可以创建新的子任务。这可以创建一种依赖关系,这样取消父任务,也会取消其子任务。
具体看案例:
public static class PracticeTask
{
public static void TaskMethod(object o)
{
Log(o?.ToString());
}
private static object s_logLock = new object();
public static void Log(string title)
{
//同步
lock (s_logLock)
{
Console.WriteLine(title);
Console.WriteLine($"任务ID:{Task.CurrentId?.ToString() ?? "无任务"},线程:{Thread.CurrentThread.ManagedThreadId}");
//线程API IsThreadPoolThread不可用于.net core 1.0运行库
#if (!DNXCORE)
Console.WriteLine($"是集合线程:{Thread.CurrentThread.IsThreadPoolThread}");
#endif
Console.WriteLine($"是后台线程:{Thread.CurrentThread.IsBackground}");
}
}
/// <summary>
/// 使用线程池中的任务启动
/// </summary>
public static void TasksUsingThreadPool()
{
var tf = new TaskFactory();
Task t1 = tf.StartNew(TaskMethod, "使用任务工厂");
Task t2 = Task.Factory.StartNew(TaskMethod, "通过任务工厂");
var t3 = new Task(TaskMethod, "使用任务构造函数并启动");
t3.Start();
Task t4 = Task.Run(() => TaskMethod("使用运行方法"));
//从上面执行的代码可以看出,要启动任务,可以使用TaskFactory类或Task类。Task类的构造函数在创建任务上提供的灵活性较大;因为调用的方法使用了lock关键字进行同步处理,所以不会出现交叉
}
/// <summary>
/// 同步任务且不使用线程池中的线程
/// </summary>
public static void RunSynchronousTask()
{
TaskMethod("只是主线");
var t1 = new Task(TaskMethod, "同步运行");
t1.RunSynchronously();
//执行过后可以看出主线程没有任务ID,也不是线程池中的线程。调用RunSynchronously()方法时,会使用相同的线程作为主调线程,但是以前没有任务,就会创建一个任务
}
/// <summary>
/// 使用单独线程池中的任务
/// </summary>
public static void LongRunningTask()
{
//TaskCreationOptions.LongRunning指示此任务的代码将长时间运行
//该线程可以不由线程池管理。当线程来自线程池时,任务调度器可以决定等待已经运行的任务完成,然而使用这个线程,而不是在线程池中创建一个新线程。
//对于长时间运行的线程,任务调度器会立即知道等待它们完全没有意义
var t1 = new Task(TaskMethod, "长时间运行的任务", TaskCreationOptions.LongRunning);
t1.Start();
}
public static Tuple<int, int> TaskWithResult(object division)
{
Tuple<int, int> div = (Tuple<int, int>)division;
int result = div.Item1 / div.Item2;
int reminder = div.Item1 % div.Item2;
Console.WriteLine("任务创建结果...");
return Tuple.Create(result, reminder);
}
/// <summary>
/// Future(TPL)——任务结果
/// </summary>
public static void TaskWithResultDemo()
{
var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create(8, 3));
t1.Start();
Console.WriteLine(t1.Result);
//等待任务完成 才可执行后续的结果
t1.Wait();
Console.WriteLine($"任务结果:{t1.Result.Item1} {t1.Result.Item2}");
}
private static void DoOnFirst()
{
Console.WriteLine($"做一些工作 {Task.CurrentId}");
Task.Delay(3000).Wait();
}
private static void DoOnSecond(Task t)
{
Console.WriteLine($"任务 {t.Id} 结束");
Console.WriteLine($"次任务ID {Task.CurrentId}");
Console.WriteLine("做一些清理工作");
Task.Delay(3000).Wait();
}
private static void DoOnError(Task t)
{
Console.WriteLine("Error:" + t.Id);
}
/// <summary>
/// 连续任务(ContinueWith)
/// </summary>
public static void ContinuationTasks()
{
//创建第一个任务
Task t1 = new Task(DoOnFirst);
//t1执行过后,执行t2任务
Task t2 = t1.ContinueWith(DoOnSecond);
//t1执行过后,执行t3任务
Task t3 = t1.ContinueWith(DoOnSecond);
//t2执行过后,执行这个任务
Task t4 = t2.ContinueWith(DoOnSecond);
//t1执行失败的情况返回的结果
Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);
//启动任务
t1.Start();
}
/// <summary>
/// 创建父任务
/// </summary>
private static void ParentTask()
{
Console.WriteLine($"任务ID {Task.CurrentId}");
//调用子任务
var child = new Task(ChildTask);//TaskCreationOptions.AttachedToParent
child.Start();
Task.Delay(1000).Wait();
Console.WriteLine("父级开始的子级");
}
/// <summary>
/// 创建子任务
/// </summary>
private static void ChildTask()
{
Console.WriteLine("子级");
Task.Delay(5000).Wait();
Console.WriteLine("子级结束");
}
/// <summary>
/// 任务层次结构(如果父任务在子任务之前结束,父任务状态就显示WaitingForChildrenToComplete。所有的子任务也结束时,父任务的状态就变成RanToCompletion
/// 当然,如果父任务用TaskCreationOptions.AttachedToParent创建一个任务时,这就无效)
/// </summary>
public static void ParentAncChild()
{
var parent = new Task(ParentTask);
parent.Start();
Task.Delay(2000).Wait();
Console.WriteLine(parent.Status);
Task.Delay(4000).Wait();
Console.WriteLine(parent.Status);
}
/// <summary>
/// 从方法中返回任务
/// </summary>
/// <returns></returns>
public static Task<List<string>> TaskMethodAsync()
{
//Task.FromResult用来创建一个带返回值的、已完成的Task。
return Task.FromResult(new List<string>() { "one", "two" });
}
public static void DoOnThird()
{
Task.Delay(5000).Wait();
Console.WriteLine($"做一些工作Third {Task.CurrentId}");
}
public static void DoOnFour()
{
Task.Delay(5000).Wait();
Console.WriteLine($"做一些工作DoOnFour {Task.CurrentId}");
}
public static void DoOnFive(Task t)
{
Task.Delay(1000).Wait();
Console.WriteLine($"做一些工作DoOnFive {Task.CurrentId}");
}
/// <summary>
/// 等待任务
/// </summary>
public static void TaskWaitMethod()
{
TaskFactory task = new TaskFactory();
Task t1 = task.StartNew(DoOnFirst);
Task t2 = Task.Run(() => DoOnThird());
//有任意一个任务执行就会执行下面的操作
Task.WaitAny(t1, t2);
Console.WriteLine("WaitAny:已执行");
//等待所有工作完成
Task.WaitAll(t1, t2);
Console.WriteLine("WaitAll:已处理");
Task t3 = Task.Run(() => DoOnFour());
Task.WhenAll(t3).ContinueWith(DoOnFive).Wait();
Console.WriteLine("WhenAll:已执行");
//whenAny类似waitany功能同whenall
}
/// <summary>
/// Parallel.For()方法的取消
/// </summary>
public static void CancelParallelFor()
{
var cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("token取消"));
cts.CancelAfter(500);
try
{
ParallelLoopResult result = Parallel.For(0, 100, new
ParallelOptions
{
CancellationToken = cts.Token,
}, x =>
{
Console.WriteLine($"环 {x} 开始");
int sum = 0;
for (int i = 0; i < 100; i++)
{
Task.Delay(2).Wait();
sum += i;
}
Console.WriteLine($"环 {x} 结束");
});
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
}
//取消任务
public static void CancelTask()
{
var cts = new CancellationTokenSource();
cts.Token.Register(() => Console.WriteLine("Token取消"));
//500秒后取消
cts.CancelAfter(500);
Task t1 = Task.Run(() =>
{
Console.WriteLine("任务中");
for (int i = 0; i < 20; i++)
{
Task.Delay(100).Wait();
CancellationToken token = cts.Token;
if (token.IsCancellationRequested)
{
Console.WriteLine("已从任务中取消请求");
//抛出异常
token.ThrowIfCancellationRequested();
break;
}
Console.WriteLine("环内");
}
Console.WriteLine("未取消的任务已完成");
}, cts.Token);
try
{
t1.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine($"异常:{ex.GetType().Name},{ex.Message}");
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine($"内部异常:{ex.InnerException.GetType()},{ex.InnerException.Message}");
}
}
}
}