.NET多线程(四)任务
什么是任务?
System.Threading.Tasks.Task
任务代表一个异步操作,是未来的操作
任务非常强大,极大的简化了异步编程,并且能带来显著的性能提升,一旦你发现了她的好处,你会根本停不下来的爱上她
举个简单例子,执行一个操作,我们要查询两张表的数据,我们创建两个任务分别各自查询一张表,是不是快些呢?
任务的线程是后台线程
任务解决的问题是什么?
线程池的缺点
(1)从线程池获取结果并不方便
(2)异常处理不太方便
(3)连续的异步操作不太方便
思想的衍进
(1)线程池是对线程的抽象,让程序员不用关心线程的创建,销毁
(2)任务是对线程池的抽象,让程序员不用关心线程池线程的处理结果,异常,以及连续异步操作
怎么创建任务?
(1)构造函数
【Action 委托,Action<object>】
static void Main(string[] args)
{
Task task = new Task(() =>
{
// True 线程池线程
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
});
task.Start();
Console.ReadLine();
}
(2)Task.Factory.StartNew
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
// True 线程池线程
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
});
Console.ReadLine();
}
(3)返回结果
【Func<TResult> 委托,Func<object, TResult>】
static void Main(string[] args)
{
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();
}
(4)不使用线程池
TaskCreationOptions.LongRunning
备注
【当执行耗时操作时,如果使用线程池线程,会导致线程池线程的滥用】
static void Main(string[] args)
{
Task task = new Task(() =>
{
// False 不使用线程池
Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread);
}, TaskCreationOptions.LongRunning);
task.Start();
Console.ReadLine();
}
任务內等待
(1)推荐CancallationToken.WaitHandle.WaitOne()
(2)Thread.Sleep()
(3)不建议
Thread.SpinWait(10000);
等待任务
(1)task.Wait(); 会抛内部异常
(2)Task.WaitAll(task1, task2)
一个异常,全部结束并报告异常
(3)int taskIndex = Task.WaitAny(task1, task2);
任一完成即完成,返回前有异常会报告异常
任务继续
task.ContinueWith
(1)支持1个 task 接多个ContinueWith
(2)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 // 无返回值 try/catch 不管卵用
{
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
try // 有返回值,在读取 Result 时,如果任务异常,会向外抛出来
{
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);
}
任务嵌套
(1)父子任务绑定
TaskCreationOptions.AttachedToParent
父完成必须子完成,父报告子异常
(2)不绑定
我一般创建子任务,不绑定,但子任务必须处理异常并确保完成。
任务调度
System.Threading.Tasks.TaskScheduler
【默认任务调度器,使用线程池线程】
TaskScheduler.FromCurrentSynchronizationContext()
【解决在UI线程操作控件】