在学task类之前必须学习线程的知识。
以下是task命名空间的类的结构图
1、2种任务类型: 有返回值task
2、2座任务工厂 TaskFactory/TaskFactory
3、2种TaskCompletionSource/TaskCompletionSource
4、3种类型的TaskScheduler任务调度器
6、8个TaskStatu 任务状态
7、15种后续任务选项TaskContinuationOptions
8、【C# Task】System.Threading.Channels 生产者和消费者模式
10、取消任务 CanellationTokenSource
11、任务并行
12、异步任务 async/await
任务并行(task parallelism)是 PFX 中最底层的并行方式。这一层次的类定义在System.Threading.Tasks
命名空间中,如下所示:
类
作用
管理工作单元
管理有返回值的工作单元
创建任务
创建有相同返回类型的任务和任务延续
管理任务调度
手动控制任务的工作流
手动控制任务的工作流
valueTask
[`<TResult>`](https://blog.gkarch.com/threading/part5.html#creating-and-starting-tasks)
手动控制任务的工作流
本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem
使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task
工作的更有效率(一般来说)。
本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem
使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task
工作的更有效率(一般来说)。
需要并行执行代码时都可以使用任务。然而,它们是为了充分利用多核而调节的:事实上,Parallel
类和PLINQ内部就是基于任务并行构建的。
任务并不只是提供了简单高效的使用线程池的方式。它们还提供了一些强大的功能来管理工作单元,包括:
任务也实现了局部工作队列(local work queues),这个优化能够让你高效的创建很多快速执行的子任务,而不会带来单一工作队列会导致的竞争开销。
使用场合:TPL 可以让你使用极小的开销创建几百个(甚至几千个)任务,但如果你要创建上百万个任务,那需要把这些任务分成大一些的工作单元才能有效率。Parallel
类和 PLINQ 可以自动实现这种工作分解。
(1)默认start()方法调用TaskScheduler.Current 任务调度器。如果要指定调度器请使用Start(TaskScheduler)。TaskScheduler.Current 是task默认的任务调度器,因为任务内部会创建子任务,如果是ui的调度器 就利用消息机制,如果是线程池就用线程池任务调度器。
Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingToRun。
//相当于:Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); task状态为WaitingForRun。
而默认的TaskScheduler采用的是.NET线程池ThreadPool,它主要面向的是细粒度的小任务,其执行时间通常在毫秒级。
Task.Run已经完成配置,直接使用。它的默认配置是:Task.Factory.StartNew(someAction , CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
TaskFactory.StartNew(工厂创建)/Task.Run() 方法是用于创建和计划计算的任务的首选的机制。该创建的任务处于WaitingToRun状态(注意:异步任务总是 WaitingForActivation)。Task.Run()轻型的TaskFactory.StartNew。
注意:TPL ( Task-Parallel-Library )、Task类(class)和 TaskStatus枚举是在 async-await 关键字引入之前就已经存在了。
async-await 引入后任务会多一种状态,但是又不能去修改之前发布的TaskStatus枚举。所以结合异步的基本处于等待状态的特性。
就将异步任务的运行状态表示为WaitingForActivation状态。
var task1 = new Task(() =>
{
//相当于:Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); 状态为WaitingForRun。
});
task1.Start();
///异步总是 WaitingForActivation
//详细解释 https://stackoverflow.com/questions/20830998/async-always-waitingforactivation
var t = Task.Run(async delegate
{
await Task.Delay(5000);
return 42;
});
Console.WriteLine(t.Status);///异步总是 WaitingForActivation
t.Wait();
Console.WriteLine(t.Status);
Console.WriteLine("Task t Status: {0}, Result: {1}",
t.Status, t.Result);
这种方式创建的任务,依附于主任务。这种方式创建的任务处于WaitingForActivation状态。Task.ContinueWith()内部用默认的TaskScheduler.Current,触发显示的指定任务计划否则都用默认的。
Task main = Task.Run(
() => {
Console.WriteLine($"{Environment.CurrentManagedThreadId} running");
Thread.Sleep(2000);
}
) ;
Task task1=main.ContinueWith(ts => Console.WriteLine("sub task ")) ;
Console.WriteLine(task1.Status);
//输出 WaitingForActivation
(1)、有两座工厂一个是泛型工厂TaskFactory
用工厂模式创建任务第一步就是配置工厂设置,两座工厂的配置都是一样的,也可采用默认的设置。
TaskFactory() 采用默认模式工厂设置。默认工厂设置TaskCreationOptions.None,TaskContinuationOptions.None ,CancellationToken.None, TaskScheduler.Scheduler=null 创建任务时候使用TaskScheduler.Current。
TaskFactory(CancellationToken)单独设定一个CancellationToken,其他配置采用默认工厂设置
TaskFactory(TaskScheduler) 单独设定一个任务调度器,其他配置采用默认工厂设置
TaskFactory(TaskCreationOptions, TaskContinuationOptions),其他配置采用默认工厂设置
TaskFactory(CancellationToken, TaskCreationOptions, TaskContinuationOptions, TaskScheduler)全部重新设置
(2)、利用默认配置或者重新配置过的设置,创建任务。
(3)、Task.Factory.StartNew() 采用的是 默认的工厂设置。
1、var task = Task.Run(() => Console.WriteLine("cancelled"));
2、 var task2 = Task.Factory.StartNew(() =>
{
//TODO you code
});
3、var t = Task
// Just loop.
int max = 1000000;
int ctr = 0;
for (ctr = 0; ctr <= max; ctr++)
{
if (ctr == max / 2 && DateTime.Now.Hour <= 12)
{
ctr++;
break;
}
}
return ctr;
});
Console.WriteLine("Finished {0:N0} iterations.", t.Result);
老的
新的
描述
task.Wait
await task
等待/等待任务完成
task.Result
await task
获取完成任务的结果
Task.WaitAny
await Task.WhenAny
等待/等待一组任务中的一个完成
Task.WaitAll
await Task.WhenAll
等待/等待一组任务中的每一个完成
Thread.Sleep
await Task.Delay
等待/等待一段时间
Task constructor
Task.Run or TaskFactory.StartNew
创建基于代码的任务
Wait 阻塞
WaitAll 返回bool
WaitAny 返回任务数组的第一完成任务索引(包括取消、错误、完成)
WhenAll 返回数组或者集合任务结果(包括取消、错误、完成)
WhenAny 返回数组或者集合任务第一完成任务(包括取消、错误、完成)
Run():静态方法线程池线程运行
RunSynchronously()/RunSynchronously(TaskScheduler):实例方法当前线程运行,可以传入任务调度器,或者采用默认线程池任务调度器.通过调用 RunSynchronously() 方法执行的任务必须是 Task 或 Task
Start ()/Start(TaskScheduler):实例方法 运行任务,可以传入任务调度器,或者采用默认线程池任务调度器。通过调用Start()方法执行的任务必须是 Task 或 Task
//在当前线程运行任务
Console.WriteLine("app threadID:" + Environment.CurrentManagedThreadId);
Task tasksync = new Task(() => Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}")) ;
tasksync.RunSynchronously();
//采用默认的线程池线程
Task task = new(()=> Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"));
task.Start();
//采用默认的线程池线程
Task.Run(() => Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}"));
Console.Read();
AsyncState
指定状态对象
当创建任务实例或调用Task.Factory.StartNew
时,可以指定一个状态对象(state object),它会被传递给目标方法。如果你希望直接调用方法而不是 lambda 表达式,则可以使用它。
static void Main()
{
var task = Task.Factory.StartNew (Greet, "Hello");
task.Wait(); // 等待任务结束
}
static void Greet (object state) { Console.Write (state); } // 打印 "Hello"
因为 C# 中有 lambda 表达式,我们可以更好的使用状态对象,用它来给任务赋予一个有意义的名字。然后就可以使用AsyncState
属性来查询这个名字:
static void Main()
{
var task = Task.Factory.StartNew (state => Greet ("Hello"), "Greeting");
Console.WriteLine (task.AsyncState); // 打印 "Greeting"
task.Wait();
}
static void Greet (string message) { Console.Write (message); }
Visual Studio 会在并行任务窗口显示每个任务的AsyncState
属性,所以指定有意义的名字可以很大程度的简化调试。
内部用有一个TimerQueueTimer定时器,该定时器内部有一个TimerQueue类型的定时器数组,数组大小等于cpu数。等到时间到了,就通知线程池执行定时委托任务,然后将该定时器从队列中删除。
Delay(Int32, CancellationToken) 创建一个在指定的毫秒数后完成的可取消任务。
Delay(TimeSpan, CancellationToken) 创建一个在指定的时间间隔后完成的可取消任务。
Delay(Int32) 创建一个在指定的毫秒数后完成的任务。
Delay(TimeSpan) 创建一个在指定的时间间隔后完成的任务。
1.Task.Delay实质是创建一个任务,再任务中开启一个定时间,然后延时指定的时间
2.Task.Delay不和await一起使用情况,当代码遇到Task.Delay一句时,创建了了一个新的任务去执行延时去了,当前代码继续往下执行,这情况task.wait 是不包含Task.Delay的时间的。
3.Task.Delay和await一起使用,当代码遇到await Task.Delay时候,当前线程要等该行代码执行完成后,再继续执行后面的代码
//同步阻塞
Task task = new( ()=> {
Task.Delay(5000).Wait();//同步阻塞 只有直接到了,才会继续执行后面的代码。
//不阻塞 创建了一个新的任务去执行延时去了,当前代码继续往下执行
Task.Delay(5000);
Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}");
});
//异步阻塞
Task task2 = new(async () => {
await Task.Delay(5000) ;
Console.WriteLine($"taskID:{Task.CurrentId} taskThreadID:{Environment.CurrentManagedThreadId}");
// 不会等待异步或者同步的Task.Delay方法,也可说Wait没达到预期效果。
});
task.Start();//task.Wait();达到预期效果
task2.Start();//task.Wait();不能达到预期效果
Console.ReadLine();
Task.Delay(),Task.Delay是基于计时器的等待机制,使用系统计时器,系统定时器的滴答速度约为16ms(windows 为15ms)。这意味着,如果参数msecondsdelay小于系统时钟的分辨率(在Windows系统上大约为15毫秒),那么时间延迟将大约等于系统时钟的分辨率。如果查看源代码,您会找到对Timer类的引用,该类负责延迟。另一方面,Thread.Sleep实际上使当前线程进入休眠状态,这样你只是阻塞并浪费一个线程。在异步编程模型中,如果您希望在延迟一段时间后发生某些事情(延续),则应始终使用Task.Delay()。await Task.Delay()释放线程做其他事情,直到计时器到期,100%清除。
使用Task.Delay,您始终可以提供取消令牌并优雅地将其删除。这就是我选择Task.Delay的一个原因。
异步代码的一个主要优点是允许一个线程同时处理多个任务,避免阻塞调用。这避免了对大量单个线程的需求,并允许线程池一次为多个请求提供服务。但是,鉴于异步代码通常在线程池上运行,不必要地使用Thread.Sleep()阻塞单个线程会占用整个线程,否则可能会在其他地方使用。如果使用Thread.Sleep()运行许多任务,则很有可能耗尽所有线程池线程并严重阻碍性能。在线程池线程中运行Thread.Sleep()是一个不好的做法。
//使用方式一,在异步中使用
public static Task ShortDelay(TimeSpan delay)
{
await Task.Delay(delay);
Console.WriteLine(string.Format("延迟{0}", delay));
}
//使用方式二,在同步代码中使用,没任何效果
Task.Factory.StartNew(() =>
{
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ====== 开始Delay()");
for (int i = 101; i < 120; i++)
{
Task ass= Task.Delay(5000);
ass.ContinueWith(t => Console.WriteLine("fdfdf"));
Console.WriteLine(ass.Id);
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ===Delay=== " + i);
Task.Delay(100);//fang'hui
}
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ====== 结束Delay()");
});
C#中的Task.Delay()和Thread.Sleep()
等待一个任务完成,都是实例方法
void Wait()
无限等待 Task 完成执行过程。
【异常】 1、任务被释放 2、任务被取消 3、任务内部抛出异常
bool Wait(Int32)
【参数】Int32 等待 Task 在指定的毫秒数内完成执行,或为 Infinite (-1),表示无限期等待。 超时返回false
【异常】1、任务被释放 2、参数Int32超出范围int.MaxValue>Time>=-1 3、任务内部抛出异常 4、任务被取消
bool Wait(Int32, CancellationToken)
【参数】Int32 等待 Task 完成执行过程。 如果在任务完成之前超时间隔结束或取消标记已取消,等待将终止,或为 Infinite (-1),表示无限期等待。。超时返回false
【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常 4、Wait(CancellationToken) CancellationToken被取消 5、参数Int32超出范围int.MaxValue>Time>=-1
bool Wait(TimeSpan)
【参数】TimeSpan在指定的时间间隔内完成任务才会返回true,否则放回false。表示等待的毫秒数的TimeSpan,或者表示-1毫秒的TimeSpan,表示无限期等待。内部转化成Wait(Int32, CancellationToken)等待 。超时返回false
【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常 4、参数TimeSpan超出范围int.MaxValue>Time>=-1
void Wait(CancellationToken)
【参数】CancellationTokenTask 完成执行过程 无限等待直到取消 。
【异常】1、任务被释放 2、任务被取消 3、任务内部抛出异常 4、Wait(CancellationToken) CancellationToken被取消
void Wait() 案例
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task task4 = Task.Run(
() => {
Console.WriteLine("任务4开始");
while (true)
{
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
Task.Delay(2000).Wait();
}
}, cts.Token
);
cts.Cancel();
try {
// 1、任务被释放 2、任务被取消 3、任务内部抛出异常
task4.Wait();
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
Console.Read();
`void` Wait(CancellationToken)**案例**
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task task4 = Task.Run(
() => {
Console.WriteLine("任务4开始");
while (true)
{
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
Task.Delay(2000).Wait();
}
}
);
cts.Cancel();
try {
// 1、任务被释放 2、任务被取消 3、任务内部抛出异常 4、Wait(CancellationToken) 触发取消
task4.Wait(ct);
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
Console.Read();
public bool Wait (int millisecondsTimeout)案例
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task task4 = Task.Run(
() => {
Console.WriteLine("任务4开始");
while (true)
{
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
Task.Delay(2000).Wait();
}
},ct
);
//cts.Cancel();
try {
// 1、任务被释放 2、时间超出范围 3、任务内部抛出异常 4、任务被取消
task4.Wait(100);//超时返回false
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
Console.Read();
等待一组任务完成,都是静态方法
public static void WaitAll (params Task[] tasks)
【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
【参数】int millisecondsTimeout 表示所有的数组中的任务在规定的 时间内完成 才返回true,否则返回false。 Infinite (-1),表示无限期等待。超时返回false
【异常】1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、参数millisecondsTimeout 超出范围int.MaxValue>Time>=-1
[System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
【特性】UnsupportedOSPlatform("browser"):表示该api不被浏览器支持。
【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、WaitAll(arrtasks, ct)方法ct 取消
public static bool WaitAll ( Task[] tasks, TimeSpan timeout)
【参数】TimeSpan timeout:在时间间隔内等待(例如:TimeSpan.FromMilliseconds(1000) 表示 1s) 或者表示-1毫秒的TimeSpan,表示无限期等待。 内部也是转化成public static bool WaitAll (Task[] tasks, int millisecondsTimeout)。超时 返回后false。
【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、参数timeout 超出范围int.MaxValue>Time>=-1
[System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken);
【特性】UnsupportedOSPlatform("browser"):表示该api不被浏览器支持。
【参数】int millisecondsTimeout 所有的数组中的任务在规定的 时间内完成 才返回true,否则返回false。 Infinite (-1),表示无限期等待。
【异常】 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、参数millisecondsTimeout超出范围int.MaxValue>Time>=-1 7、参数cancellationToken取消
public static void WaitAll (params Task[] tasks)案例
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
Task task3 = Task.Run(() => { Console.WriteLine("任务3开始"); Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
Task task4 = Task.Run(
() => {
Console.WriteLine("任务4开始");
while (true)
{
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
Task.Delay(2000).Wait();
}
}, cts.Token
);
cts.Cancel();
Task[] arrtasks = new[] { task1, task2, task4 };
try {
// 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常
bool dd =Task.WaitAll(arrtasks);//写法二 Task.WaitAll(task1, task2, task3);
//
Console.WriteLine(dd);
}catch (Exception ex) { }
Console.Read();
public static bool WaitAll (Task[] tasks, int millisecondsTimeout)案例
//其他代码同上
try {
// 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素 4、任务被取消了 5、任务内部抛出异常 6、时间超出范围
bool waitStatus =Task.WaitAll(arrtasks, 20000);//wait超时 返回后false。
Console.WriteLine(waitStatus);
}catch (Exception ex) { }
public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)案例
//其他代码同上
try {
// 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
// 4、任务被取消了 5、任务内部抛出异常 6、WaitAll(arrtasks, ct)方法ct 取消
Task.WaitAll(arrtasks, ct);
}
catch (Exception ex) { }
public static bool WaitAll ( Task[] tasks, TimeSpan timeout)案例
//其他代码同上
Task[] arrtasks = new[] { task1, task2, task4 };
TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-1);
try {
// 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
// 4、任务被取消了 5、任务内部抛出异常 6、时间超出范围X<-1 或 X>maxTime
bool outime= Task.WaitAll(arrtasks, ts);// Task.WaitAll(arrtasks, TimeSpan.FromMilliseconds(5000));//表示5s
// 超时 返回后false。
Console.WriteLine(outime );
}
catch (Exception ex) { }
public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)案例
//其他代码同上
//cts.Cancel();
Task[] arrtasks = new[] { task1, task2, task4 };
TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-1);
try {
// 捕获异常 1、数组中 >=1任务被释放了 2、tasks数组为null 3、 tasks数组包含null元素
// 4、任务被取消了 5、任务内部抛出异常 6、时间超出范围X<-1 或 X>maxTime 7、WaitAll(arrtasks,1000, ct)方法ct 取消
bool outime = Task.WaitAll(arrtasks, 1000,ct);// Task.WaitAll(arrtasks, TimeSpan.FromMilliseconds(5000));//表示5s
// 超时 返回后false。
Console.WriteLine(outime );
}
catch (Exception ex) { }
Console.Read();
等等待一组任务中 任意一个完成 返回值是数组索引。都是静态方法
static int WaitAny(Task[], TimeSpan)
【说明】等待任何提供的 Task 对象在指定的时间间隔内完成执行。任务成功返回数组索引
【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1
static int WaitAny(Task[], Int32, CancellationToken)
【说明】 等待提供的任何 Task 对象在指定的毫秒数内完成执行,或等到取消标记取消。Int32表示Task 对象在指定的毫秒数内完成执行,或表示 -1 毫秒(无限期等待) 。任务成功返回数组索引
【异常】【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1 5、参数CancellationToken 取消
static int WaitAny(Task[], CancellationToken)
【说明】 等待提供的任何 Task 对象完成执行过程(除非取消等待)。任务成功返回数组索引
【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、参数CancellationToken 取消
static int WaitAny(Task[], Int32)
【说明】等待任何提供的 Task 对象在指定的毫秒数内完成执行或表示 -1 毫秒(无限期等待)。任务成功返回数组索引
【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task[], Int32)Int32 超出范围int.MaxValue>Time>=-1
static int WaitAny(Task[])
【说明】等待提供的任一 Task 对象完成执行过程。任务成功返回数组索引
【异常】1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素
static int WaitAny(Task[], Int32)案例,其他api就不举子了,都一样
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
Task task3 = Task.Run(() => { Console.WriteLine("任务3开始"); Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
Task task4 = Task.Run(
() => {
Console.WriteLine("任务4开始");
while (true)
{
if (ct.IsCancellationRequested)
{
ct.ThrowIfCancellationRequested();
}
Task.Delay(2000).Wait();
}
}, cts.Token
);
//cts.Cancel();
Task[] arrtasks = new[] { task1, task2, task4 };
try
{
// 捕获异常 1、数组中的任务被释放 2、tasks数组是null 3、tasks数组包含空元素 4、WaitAny(Task\[\], Int32)Int32 超出范围int.MaxValue>Time>=-1
int index = Task.WaitAny(arrtasks,20000);//
Console.WriteLine("数组索引"+index);
}
catch (Exception ex) { }
Console.Read();
异步等待,实例方法,未提供int32类型Api但是可以用TimeSpan.FromMilliseconds(5000)来实现int32类型的等待。
Task WaitAsync(TimeSpan, CancellationToken) 获取一个任务,该任务将在此任务完成时、指定的超时超时时或指定的CancellationToken被请求取消时完成。
CancellationTokenSource CancellationTokenSource=new CancellationTokenSource();
CancellationTokenSource.Cancel();
Task
//异步等待2或者接收到取消信号,然后输出等待的状态
task1.WaitAsync(TimeSpan.FromMilliseconds(2000),CancellationTokenSource.Token).ContinueWith((t) => Console.WriteLine("等待任务的状态"+t.Status));
// 时间间隔内等待,等待5秒钟
//TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-5);
//task1.WaitAsync(TimeSpan.FromMilliseconds(ts)).ContinueWith((t) => Console.WriteLine("等待任务的状态" + t.Status));
Console.WriteLine("任务1的状态" + task1.Status);
Console.Read();
/*输出
任务1开始
任务1的状态Running
等待任务的状态Canceled
任务1完成
*/
Task WaitAsync(TimeSpan) 返回一个任务A,该任务将在B任务完成后 或指定超时超时时完成的任务。
Task
//异步等待2s,然后输出等待的状态 未提供int32类型Api但是可以用TimeSpan.FromMilliseconds(5000)来实现int32类型的等待。
task1.WaitAsync(TimeSpan.FromMilliseconds(2000)).ContinueWith((t) => Console.WriteLine("等待任务的状态"+t.Status));
// 时间间隔内等待,等待5秒钟
//TimeSpan ts = DateTime.Now - DateTime.Now.AddSeconds(-5);
//task1.WaitAsync(TimeSpan.FromMilliseconds(ts)).ContinueWith((t) => Console.WriteLine("等待任务的状态" + t.Status));
Console.WriteLine("任务1的状态" + task1.Status);
Console.Read();
/*输出
任务1开始
任务1的状态Running
等待任务的状态Faulted
任务1完成
*/
Task WaitAsync(CancellationToken) 返回一个任务A,该任务将在B任务完成后完成
等待一组任务完成,返回值是task,都是静态方法
static WhenAll(IEnumerable
创建一个任务,该任务将在可枚举集合中的所有task对象都完成时完成。
调用WhenAll(IEnumerable
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
static WhenAll(params
Task[])
创建一个任务,该任务将在数组中的所有task对象都完成时完成。
调用WhenAll(Task[])方法不会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
static Task
创建一个任务,该任务将在可枚举集合中的所有task 对象都完成时完成。
调用WhenAll(IEnumerable>)方法不会阻塞调用线程。但是,对返回的Result属性的调用会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。任务<
TResult
>。返回任务的Result属性将被设置为一个数组,其中包含所提供任务的所有结果,顺序与提供的顺序相同(例如,如果输入任务数组包含t1,
t2, t3,则输出任务的task 。Result属性将返回一个TResult[],其中arr[0] == t1。结果,arr[1] ==
t2。和arr[2] == t3.Result)。
如果tasks参数不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。返回的TResult[]将是一个包含0个元素的数组。
static Taskparams
Task
创建一个任务,该任务将在数组中的所有task 对象都完成时完成。
以上案例如下:
static WhenAll(IEnumerable
var tasklist=new List
Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
tasklist.Add(task1);
tasklist.Add(task2);
tasklist.Add(task3);
Task taskswait = Task.WhenAll(tasklist);
try
{
//等待期间可能发送错误、取消等 所有要捕获异常
taskswait.Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
注意
调用WhenAll(IEnumerable
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
static WhenAll(params
Task[]) 案例
Task task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); });
Task task2 = Task.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); });
Task task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); });
Task[] arrtasks = new[] { task1 , task2 , task3 };
Task taskswait= Task.WhenAll(arrtasks);
try
{
//等待期间 任务可能取消、发生异常等,所有要捕获异常
taskswait.Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
注意
调用WhenAll(Task[])方法不会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
WhenAll
var tasklist=new List
Task
Task
Task
tasklist.Add(task1);
tasklist.Add(task2);
tasklist.Add(task3);
Task
try
{
//等待期间可能发送错误、取消等 所有要捕获异常
taskswait.Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
if (taskswait.Status == TaskStatus.RanToCompletion)
{
foreach (var item in taskswait.Result)
{
Console.WriteLine(item);
}
}
else
{
}
Console.Read();
注意
调用WhenAll(IEnumerable>)方法不会阻塞调用线程。但是,对返回的Result属性的调用会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。任务< TResult >。返回任务的Result属性将被设置为一个数组,其中包含所提供任务的所有结果,顺序与提供的顺序相同(例如,如果输入任务数组包含t1, t2, t3,则输出任务的task 。Result属性将返回一个TResult[],其中arr[0] == t1。结果,arr[1] == t2。和arr[2] == t3.Result)。
如果tasks参数不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。返回的TResult[]将是一个包含0个元素的数组。
等待一组任务完成,返回值是Task
static Task
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
static Task
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
static Task
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。
static Task
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。
static Task
创建一个任务,该任务将在两个任务中的任何一个任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
**
static Task
var tasklist=new List
Task
Task
Task
tasklist.Add(task1);
tasklist.Add(task2);
tasklist.Add(task3);
Task
try
{
//等待期间可能发送错误、取消等 所有要捕获异常
taskswait.Wait();
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
if (taskswait.Status == TaskStatus.RanToCompletion)
{
Console.WriteLine("返回的任务ID:"+ taskswait.Result.Id);
}
else
{
}
Console.Read();
Task.Yield
await Task.Yield和Thread.yield 一个意思,让出一下当前task线程,让有需要的任务先运行,当前线程没有其他任务 那么他就继续执行。 会捕获同步上下文,如果同步上下文为null。就使用当前TaskScheduler。
源代码:
SynchronizationContext? syncCtx = SynchronizationContext.Current;
if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
{
syncCtx.Post(s_sendOrPostCallbackRunAction, continuation);
}
else
{
// If we're targeting the default scheduler, queue to the thread pool, so that we go into the global
// queue. As we're going into the global queue, we might as well use QUWI, which for the global queue is
// just a tad faster than task, due to a smaller object getting allocated and less work on the execution path.
TaskScheduler scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Task.ConfigureAwait(false)
表示await/async`异步中`保证回调不会被排队回到原始上下文中。ConfigureAwait(false)在框架库中使用,通用库是“通用的”,部分原因是它们不关心使用它们的环境。在ui环境下``await/async`异步中` ``避免使用ConfigureAwait(false)。异步使用会导致后续代码无法回到主线程,导致bug,如下错误用法:
private static readonly HttpClient s_httpClient = new HttpClient();
private async void downloadBtn_Click(object sender, RoutedEventArgs e)
{
string text = await s_httpClient.GetStringAsync("http://example.com/currenttime").ConfigureAwait(false); // bug
downloadBtn.Content = text;//将在默认同步上下文中执行 bug
}
创建一个返回值为TResult类值的Task实列。
Task
{
return Task.FromResult(1123);
}
Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult());
//输出:1123
在通过task.Result和task.GetAwaiter().GetResult()获取该类实列的结果时,将触发异常。
Task
{
return Task.FromException
}
try
{//将触发异常
string sdf= GetCustomerIdAsync().Result;
}
// Ignore exceptions here.
catch (AggregateException) { }
Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult() );//将触发异常
返回一个带有取消标记的task实列。尚未对 cancellationToken
请求取消;其 IsCancellationRequested 属性为 false
。
CancellationTokenSource cts=new CancellationTokenSource();
Task
{
return Task.FromCanceled
}
//要执行,
cts.Cancel();
Console.WriteLine(GetCustomerIdAsync().Status);//如果不执行cts.Cancel();将触发ArgumentOutOfRangeException异常
可在创建Task时将一个CancellationToken传给构造器,从而将两者相关联,如果CancellationToken在Task调度前取消,那么Task就会被取消,永远都不执行。但如果Task已调度,那么Task的代码就只支持显示取消,其操作才能在执行期间取消,遗憾的是,虽然Task关联了一个CancellationToken,但却没有办法访问他。因此,必须在Task的代码中获得创建Task对象时的同一个CancellationToken。为此,最简单的办法就是使用一个Lamda表达式,将CancellationToken作为闭包变量传递。
只需要多个task使用相同的CancellationTokenSource.Token即可,将上面的代码稍微改动下:
public static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
for (int i = 0; i < 5; i++)
{
var msg = "xiaoming" + (i + 1);
var task = new Task(() => Run(msg, cts.Token), cts.Token);
task.Start();
}
Thread.Sleep(5000);
cts.Cancel();
Console.WriteLine("ok!");
Console.ReadLine();
}
这种应用的场景为:当有多个CancellationTokenSource用来作用于一个异步任务的时候,你想达到其中一个CancellationTokenSource取消就取消这个异步任务的效果。
看下面代码:
using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace TestDI
{
class Program
{
public static void Main(string[] args)
{
CancellationTokenSource cts1 = new CancellationTokenSource();
CancellationTokenSource cts2 = new CancellationTokenSource();
CancellationTokenSource cts3 = new CancellationTokenSource();
CancellationTokenSource compositeCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token, cts3.Token);
var task = new Task(() => Run("xiaoming", compositeCts.Token), compositeCts.Token);
task.Start();
Thread.Sleep(5000);
cts1.Cancel();
Console.WriteLine("ok!");
Console.ReadLine();
}
public static void Run(object state, CancellationToken token)
{
while (true)
{
if (token.IsCancellationRequested)
{
return;
}
else
{
Thread.Sleep(1000);
Console.WriteLine($"state={state},任务线程:{Thread.CurrentThread.ManagedThreadId},是否是守护线程:{Thread.CurrentThread.IsBackground},是否是线程池:{Thread.CurrentThread.IsThreadPoolThread}");
}
}
}
}
}
ConfigureAwait(false)是否保证回调不会在原始上下文中运行?
不。它保证它不会被排队回到原始上下文中……但这并不意味着await task.ConfigureAwait(false)
之后的代码仍无法在原始上下文中运行。那是因为等待已经完成的等待对象只是保持await
同步运行,而不是强迫任何东西排队。因此,如果您await
的任务在等待时已经完成,无论您是否使用过ConfigureAwait(false)
,紧随其后的代码将在当前上下文中继续在当前线程上执行。
//当前线程
Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread+Environment.CurrentManagedThreadId);
//第一种方式 Continuetask和第taskA在同一个线程运行
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
if (SynchronizationContext.Current != null)
{
Task taskA = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
taskA.ContinueWith((t) =>{ Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId); },TaskContinuationOptions.ExecuteSynchronously);
taskA.Start();
}
else { Console.WriteLine("当前上下文为空"); }
//第二种方式 task在当前线程运行
Console.WriteLine("是否是线程池线程:" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId);
if (SynchronizationContext.Current != null)
{
Task task = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
task.RunSynchronously();
}
Console.Read();
//第三种方式 task在当前线程运行 在ui线程中可行,控制台线程使用的默认同步上下文,不可行
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
if (SynchronizationContext.Current != null)
{
Task task2 = new(() => Console.WriteLine("是否是线程池线程" + Thread.CurrentThread.IsThreadPoolThread + Environment.CurrentManagedThreadId));
task2.Start(TaskScheduler.FromCurrentSynchronizationContext());
}
else { Console.WriteLine("当前上下文为空"); }
手机扫一扫
移动阅读更方便
你可能感兴趣的文章