什么是任务?
任务代表一个异步操作,是未来的操作。
任务的线程是后台线程。
任务非常强大,极大的简化了异步编程,并且能带来显著的性能提升,一旦你发现了她的好处,你会根本停不下来的爱上她。
举个简单例子,执行一个操作,我们要查询两张表的数据,我们创建两个任务分别各自查询一张表,是不是快些呢?
System.Threading.Tasks.Task
任务解决的问题是什么?
线程池的缺点
- 从线程池获取结果并不方便
- 异常处理不太方便
- 连续的异步操作不太方便
思想的衍进
线程池是对线程的抽象
程序员不用关心线程的创建,销毁。
任务是对线程池的抽象
程序员不用关心线程池线程的处理结果,异常,以及连续异步操作。
怎么创建任务?
构造函数
static void Main(string[] args)
{
// 传入 Action 委托,Action<object>
Task task = new Task(() =>
{
// True 线程池线程
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
});
task.Start();
Console.ReadLine();
}
工厂创建 Task.Factory.StartNew
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
// True 线程池线程
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
});
Console.ReadLine();
}
返回结果
static void Main(string[] args)
{
// 传入 Func<TResult> 委托,Func<object, TResult>
Task<string> task = new Task<string>(() =>
{
Thread.Sleep(3 * 1000);
// True 线程池线程
return Thread.CurrentThread.IsThreadPoolThread.ToString();
});
task.Start();
task.Wait(); // 等待任务完成
Console.WriteLine(task.Result); // 打印结果
Console.ReadLine();
}
不使用线程池线程
TaskCreationOptions.LongRunning
当执行耗时操作时,如果使用线程池线程,会导致线程池线程的滥用
static void Main(string[] args)
{
Task task = new Task(() =>
{
// False 不使用线程池
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
}, TaskCreationOptions.LongRunning);
task.Start();
Console.ReadLine();
}
任务內等待
- 推荐 CancallationToken.WaitHandle.WaitOne()
- Thread.Sleep()
- 不建议 Thread.SpinWait(10000);
等待任务
- task.Wait(); 会抛内部异常
- Task.WaitAll(task1, task2) 一个异常,全部结束并报告异常
- int taskIndex = Task.WaitAny(task1, task2); 任意一个完成即完成,返回前有异常会报告异常
任务继续
task.ContinueWith
- 这个支持 1 个 task 接多个 ContinueWith
- ContinueWith 返回下一代 task,下一代可以继续 ContinueWith
static void Main(string[] args)
{
Task<string> task = new Task<string>(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return "girls,";
});
task.ContinueWith((t) =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(t.Result + "come on.");
});
task.Start();
Console.ReadLine();
}
使用 TaskContinuationOptions
static void Main(string[] args)
{
Task task = new Task(() =>
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
});
task.ContinueWith((t) =>
{
Console.WriteLine("OnlyOnRanToCompletion");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.Start();
Console.ReadLine();
}
所有/任一任务完成后继续
Task.Factory.ContineWhenAll()
Task.Factory.ContineWhenAny()
Task<TCType>.Factory.ContinueWhenAll<TAType>()
Task<TCType>.Factory.ContinueWhenAny<TAType>()
取消任务
System.Threading.CancellationTokenSource
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
Task task = new Task(() =>
{
Thread.Sleep(3 * 1000);
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
}, cts.Token);
task.ContinueWith((t) =>
{
Console.WriteLine("OnlyOnCanceled");
}, TaskContinuationOptions.OnlyOnCanceled);
task.Start();
//cts.Cancel();
Console.ReadLine();
}
任务异常
无返回值,抓不到异常
// 无返回值 try/catch 不管卵用
try
{
Task task = new Task(() =>
{
throw new Exception("error");
});
task.ContinueWith((t) =>
{
Console.WriteLine(t.Exception.InnerException.Message);
}, TaskContinuationOptions.OnlyOnFaulted); // 这样可以抓到异常
task.Start();
}
catch (Exception ex)
{
Console.WriteLine("无返回值:" + ex.Message);
}
有返回值,读取 Result
// 有返回值,在读取 Result 时,如果任务异常,会向外抛出来
try
{
Task<string> task = new Task<string>(() =>
{
throw new Exception("error");
return "girls";
});
task.Start();
Console.WriteLine(task.Result);
}
catch (AggregateException ex)
{
Console.WriteLine("有返回值:" + ex.InnerException.Message);
}
任务嵌套
父子任务绑定
TaskCreationOptions.AttachedToParent 父完成必须子完成,父报告子异常
不绑定
创建子任务,不绑定,但子任务必须处理异常并确保完成。
任务调度
System.Threading.Tasks.TaskScheduler
默认任务调度器,使用线程池线程
TaskScheduler.FromCurrentSynchronizationContext()
解决在UI线程操作控件
完。