系列1: https://blog.csdn.net/lvxingzhe3/article/details/121049576
系列2:C#及WPF多线程定义和使用2(Task)_lvxingzhe3的博客-CSDN博客
Task用的是线程池,线程池的线程数量的有上限的,这个可以通过ThreadPool修改,我们经常会用到task.run ,new task ,和task.factory.startnew方法来创建任务。
Task.Factory.StartNew(action)不是直接创建线程,创建的是任务,它有一个任务队列,然后通过任务调度器把任务分配到线程池中的空闲线程中,任务是不能被直接执行的,只有分配给线程才能被执行,如果任务的数量比线程池中的线程多,线程池的线程数量还没有到达上限,就会创建新线程执行任务。如果线程池的线程已到达上限,没有分配到线程的任务需要等待有线程空闲的时候才执行。
task 是新建一个异步任务,这个任务是分配到子线程中去的,跟我们之前的new thread,创建线程很相似,在子线程中,通过SynchronizationContext类进行上下文同步,实现子线程和主线程之间的通信。
Task的三种创建启动方法:
1.通过new方法创建,然后start启动
2.通过Task.Factory.StartNew创建(标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行)
3.通过Task.Run创建
Task task1 = new Task(Method);
task1.Start();
Task task2 = Task.Run(Method);
Task task3 = Task.Factory.StartNew(Method);
Task连续任务使用ContinueWith:
Task task = new Task(() =>
{
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());
});
task.Wait();
cwt.Wait();
Task阻塞与等待:
如果要等待任务完成可以使用Task.Wait方法,使用WaitAll方法等待所有任务完成。如果等待多个任务但是只需要任何一个任务完成就可以执行下一步,则可以使用WaitAny。
static void Main(string[] args)
{
var t1 = Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine($"t1线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
var t2 = Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine($"t2线程ID为{ Thread.CurrentThread.ManagedThreadId}");
});
//t1.Wait();//等待t1先执行
//Task.WaitAll(t1, t2);//等待t1,t2先执行
//Task.WaitAny(t1, t2);//等待t1,t2任意一个执行完,执行下面语句
var t3 = Task.Run(() =>
{
Console.WriteLine($"t3线程ID为{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine("t3 start....");
Thread.Sleep(2000);
Console.WriteLine("t3 end....");
});
Console.WriteLine("main start....");
Thread.Sleep(1000);
Console.WriteLine("main end....");
Console.ReadLine();
}
Task WhenAll和WhenAny:
var task1 = new Task(() => TaskMethod("First Task", 1));
var task2 = new Task(() => TaskMethod("Second Task", 2));
var whenAllTask = Task.WhenAll(task1, task2);
whenAllTask.ContinueWith(t =>Console.WriteLine("The first answer is {0}, the second is {1}", t.Result[0], t.Result[1]),
TaskContinuationOptions.OnlyOnRanToCompletion
);
task1.Start();
task2.Start();
Task的取消以及取消回调方法:
CancellationTokenSource的功能不仅仅是取消任务执行,我们可以使用 source.CancelAfter(5000)实现5秒后自动取消任务,也可以通过 source.Token.Register(Action action)注册取消任务触发的回调函数,即任务被取消时注册的action会被执行。
static void Main(string[] args)
{
CancellationTokenSource source = new CancellationTokenSource();
//注册任务取消的事件
source.Token.Register(() =>
{
Console.WriteLine("任务被取消后执行xx操作!");
});
int index = 0;
//开启一个task执行任务
Task task1 = new Task(() =>
{
while (!source.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine($"第{++index}次执行,线程运行中...");
}
});
task1.Start();
//延时取消,效果等同于Thread.Sleep(5000);source.Cancel();
source.CancelAfter(5000);
Console.ReadKey();
}
Task线程暂停与继续:
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ManualResetEvent resetEvent = new ManualResetEvent(true);
Task task = new Task(async () => {
while (true) {
if (token.IsCancellationRequested) {
return;
}
// 初始化为true时执行WaitOne不阻塞
resetEvent.WaitOne();
// Doing something.......
// 模拟等待100ms
await Task.Delay(100);
}
}, token);
//暂停线程
resetEvent.Reset();
//继续线程
resetEvent.Set();
Task任务的状态和返回值(Task
var t1 = Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine($"t1线程ID为{ Thread.CurrentThread.ManagedThreadId}");
return "t1 return";
});
Console.WriteLine($"t1 return value {t1.Result}");
判断Task超时的方法:(用Task.Delay(ElapsedMilliseconds, _cancellationTokenSource.Token);而不用Task.Delay(ElapsedMilliseconds); 因为后者会卡住task固定的时常,但是用前者可以随时取消)
///
/// Gets another task which that the given task can be awaited with a .
///
/// The task to be awaited.
/// The number of milliseconds to wait.
///
/// true:if the task completed execution within the allotted time; otherwise false
///
public static async Task GetTaskWithTimeout(this Task self, int timeout)
{
var timeoutTask = Task.Delay(timeout);
var finishedTask = await Task.WhenAny(self, timeoutTask);
return ReferenceEquals(finishedTask, self);
}
Task异步方法(async/await):(微软建议异步方法的命名是在方法名后添加Async后缀,用async来修饰一个方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task
class Program
{
static void Main(string[] args)
{
string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result;
//调用同步方法
//string content = GetContent(Environment.CurrentDirectory + @"/test.txt");
Console.WriteLine(content);
Console.ReadKey();
}
//异步读取文件内容
async static Task GetContentAsync(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
var bytes = new byte[fs.Length];
//ReadAync方法异步读取内容,不阻塞线程
Console.WriteLine("开始读取文件");
int len = await fs.ReadAsync(bytes, 0, bytes.Length);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
//同步读取文件内容
static string GetContent(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
var bytes = new byte[fs.Length];
//Read方法同步读取内容,阻塞线程
int len = fs.Read(bytes, 0, bytes.Length);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
}
Task子任务:
class Program
{
public static void Main()
{
Task parent = new Task(state =>
{
Console.WriteLine(state);
string[] result = new string[2];
//创建并启动子任务
new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
return result;
}, "我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
//任务处理完成后执行的操作
parent.ContinueWith(t =>
{
Array.ForEach(t.Result, r => Console.WriteLine(r));
});
//启动父任务
parent.Start();
//等待任务结束 Wait只能等待父线程结束,没办法等到父线程的ContinueWith结束
//parent.Wait();
Console.ReadLine();
}
}
Task异常处理:Task是多线程的,一个线程发生异常不会影响其他线程执行。
Task的优势:
ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
ThreadPool不支持线程执行的先后次序;
本文转载地址:
C# Task详解 - 五维思考 - 博客园
C# Task和async/await详解_btfireknight的博客-CSDN博客_async await task