C# Task实现多线程
C# Task实现多线程一、Task 的优点以及功能
1、在任务启动后,可以随时以任务延续的形式注册回调。
2、通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。
3、在同一Task 对象中封装异步 I/O 绑定和计算绑定操作。
4、监视Task 对象的状态。
5、使用TaskCompletionSource 将操作的状态封送到Task 对象。
二、创建 Task
1、使用构造函数创建
使用Task的构造函数来创建任务,并调用Start方法来启动任务并执行异步操作。
例如
static void Main(string[] args)
{
Console.WriteLine("主线程执行业务处理.");
//创建任务
Task task = new Task(() => {
Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
});
//启动任务,并安排到当前任务队列线程中执行任务 (System.Threading.Tasks.TaskScheduler)
task.Start();
Console.WriteLine("主线程执行其他处理");
//主线程挂起1000毫秒,等待任务的完成。
Thread.Sleep(1000);
}
2、使用Task.Factory.StartNew 进行创建Task
Task.Factory 是对Task进行管理,调度管理这一类的。
例如
var task1 = Task.Factory.StartNew(() => DoSomeWork()).ContinueWith(
task => { Console.WriteLine(task.Result.ToString()); }).ContinueWith(
task => { Console.WriteLine(task.Result.ToString()); });
三、等待任务的完成并获取返回值
使用任务执行异步操作时,最主要的是要后的任务完成时的返回值。在任务类中有一个实例方法Wait(有许多重载版本)他能等待任务的完成,我们也可以通过Task类的派生类Task<TResult>创建一个异步任务,并指定任务完成时返回值的类型,这样可以通过Task<TResult>的实例对象获取到任务完成后的返回值。
例如
static void TaskWait() {
//创建任务
Task<int> task = new Task<int>(() =>
{
int sum = 0;
Console.WriteLine("使用Task执行异步操作.");
for (int i = 0; i < 100; i++)
{
sum+=i;
}
return sum;
});
//启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
task.Start();
Console.WriteLine("主线程执行其他处理");
//等待任务的完成执行过程。
task.Wait();
//获得任务的执行结果
Console.WriteLine("任务执行结果:{0}", task.Result.ToString());
}
四、等待所有线程结束
var task1 = Task.Factory.StartNew(() => DoSomeWork());
var task2 = Task.Factory.StartNew(() => DoSomeWork());
var task3 = Task.Factory.StartNew(() => DoSomeWork());
Task.WaitAll(task1, task2, task3);
五、等待其中一个线程结束
var task1 = Task.Factory.StartNew(() => DoSomeWork());
var task2 = Task.Factory.StartNew(() => DoSomeWork());
var task3 = Task.Factory.StartNew(() => DoSomeWork());
Task.WaitAny(task1, task2, task3);
六、使用ContinueWith方法在任务完成时启动一个新任务
在使用能够Task类的Wait方法等待一个任务时或派生类的Result属性获得任务执行结果都有可能阻塞线程,为了解决这个问题可以使用ContinueWith方法,他能在一个任务完成时自动启动一个新的任务来处理执行结果。
static void TaskContinueWith()
{
//创建一个任务
Task<int> task = new Task<int>(() =>
{
int sum = 0;
Console.WriteLine("使用Task执行异步操作.");
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
//启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
task.Start();
Console.WriteLine("主线程执行其他处理");
//任务完成时执行处理。
Task cwt = task.ContinueWith(t => {
Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
});
Thread.Sleep(1000);
}
七、创建一组具有相同状态的任务
可以使用TaskFactory类或TaskFactory<TResult>类。这两个类创建一组任务时可以指定任务的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler默认值。
例如
static void TaskFactoryApply()
{
Task parent = new Task(() =>
{
CancellationTokenSource cts = new CancellationTokenSource(5000);
//创建任务工厂
TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//添加一组具有相同状态的子任务
Task[] task = new Task[]{
tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第一个任务。"); }),
tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第二个任务。"); }),
tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第三个任务。"); })
};
});
parent.Start();
Console.Read();
}
八、任务内部实现和任务调度
1、任务内部有一组构成任务状态的属性,标识任务的唯一Id、表示任务的执行状态(TaskStatus)、任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState、对任务创建时的任务调度对象(TaskScheduler)的引用、对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。
2、Task类和Task<TResult>类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。
3、可以使用Task类的CurrentId属性获得正在执行的任务的Id,如果没有任务在执行CurrentId返回值为null,CurrentId是一个int?可空类型的属性。
4、任务执行的生命周期通过TaskStatus类型的一个值来表示,TaskStatus所包含的值:
public enum TaskStatus
{
Created = 0,
WaitingForActivation = 1,
WaitingToRun = 2,
Running = 3,
WaitingForChildrenToComplete = 4,
RanToCompletion = 5,
Canceled = 6,
Faulted = 7,
}
5、在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类:
ThreadPoolTaskScheduler线程池任务调度器
所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。
SynchronizationContextTaskScheduler同步上下文任务调度器
SynchronizationContextTaskScheduler任务调度器能够用在Window form、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。
6、例如
private void button1_Click(object sender, EventArgs e)
{
//获得同步上下文任务调度器
TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
//创建任务,并采用默认任务调度器(线程池任务调度器)执行任务
Task<int> task = new Task<int>(() =>
{
//执行复杂的计算任务。
Thread.Sleep(2000);
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
var cts=new CancellationTokenSource();
//任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件。
task.ContinueWith(t => {this.label1.Text="采用SynchronizationContextTaskScheduler任务调度器更新UI。\\r\\n计算结果是:"+task.Result.ToString(); },
cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
task.Start();
}