【C# Task】开篇
概览
在学task类之前必须学习线程的知识。
以下是task命名空间的类的结构图
1、2种任务类型: 有返回值task<TResult> 、无返回值task。
2、2座任务工厂 TaskFactory/TaskFactory<TResult>
3、2种TaskCompletionSource/TaskCompletionSource<TResult> 任务完成源
4、3种类型的TaskScheduler任务调度器
6、8个TaskStatu 任务状态
7、15种后续任务选项TaskContinuationOptions
8、【C# Task】System.Threading.Channels 生产者和消费者模式
9、【C# Task】 ValueTask/Task<TResult>
10、取消任务 CanellationTokenSource
11、任务并行
12、异步任务 async/await
任务并行(task parallelism)是 PFX 中最底层的并行方式。这一层次的类定义在System.Threading.Tasks
命名空间中,如下所示:
类 | 作用 |
---|---|
Task |
管理工作单元 |
Task<TResult> |
管理有返回值的工作单元 |
TaskFactory |
创建任务 |
TaskFactory<TResult> |
创建有相同返回类型的任务和任务延续 |
TaskScheduler |
管理任务调度 |
TaskCompletionSource |
手动控制任务的工作流 |
valueTask |
手动控制任务的工作流 |
valueTask
|
手动控制任务的工作流 |
本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem
使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task
工作的更有效率(一般来说)。
本质上,任务是用来管理可并行工作单元的轻量级对象。任务使用 CLR 的线程池来避免启动独立线程的开销:它和ThreadPool.QueueUserWorkItem
使用的是同一个线程池,在 CLR 4.0 中这个线程池被调节过,让Task
工作的更有效率(一般来说)。
需要并行执行代码时都可以使用任务。然而,它们是为了充分利用多核而调节的:事实上,Parallel
类和PLINQ内部就是基于任务并行构建的。
任务并不只是提供了简单高效的使用线程池的方式。它们还提供了一些强大的功能来管理工作单元,包括:
任务也实现了局部工作队列(local work queues),这个优化能够让你高效的创建很多快速执行的子任务,而不会带来单一工作队列会导致的竞争开销。
使用场合:TPL 可以让你使用极小的开销创建几百个(甚至几千个)任务,但如果你要创建上百万个任务,那需要把这些任务分成大一些的工作单元才能有效率。Parallel
类和 PLINQ 可以自动实现这种工作分解。
创建Task
任务的初始化配置 :
1、构造函数new Task 创建任务
- Task(Action)
- Task(Action, CancellationToken)
- Task(Action, TaskCreationOptions)
- Task(Action<Object>, Object)
- Task(Action, CancellationToken, TaskCreationOptions)
- Task(Action<Object>, Object, CancellationToken)
- Task(Action<Object>, Object, TaskCreationOptions)
- Task(Action<Object>, Object, CancellationToken, TaskCreationOptions)
(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,它主要面向的是细粒度的小任务,其执行时间通常在毫秒级。
2、Task.Run方式创建task
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);
3、Task.ContinueWith 创建后续任务
这种方式创建的任务,依附于主任务。这种方式创建的任务处于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
4、工厂方式创建Task
(1)、有两座工厂一个是泛型工厂TaskFactory<TResult>()和普通工厂TaskFactory() ,泛型工厂有任务返回值,返回值保存在任务的Result。
用工厂模式创建任务第一步就是配置工厂设置,两座工厂的配置都是一样的,也可采用默认的设置。
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<int>.Run(() => {
// 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的任务控制
老的 | 新的 | 描述 |
---|---|---|
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/start 运行任务
Run():静态方法线程池线程运行
RunSynchronously()/RunSynchronously(TaskScheduler):实例方法当前线程运行,可以传入任务调度器,或者采用默认线程池任务调度器.通过调用 RunSynchronously() 方法执行的任务必须是 Task 或 Task<TResult> 类构造函数进行实例化。 要同步运行的任务必须处于 Created 状态。 任务只能启动并运行一次。 如果尝试再次计划任务,将导致异常。Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingtoRun。
Start ()/Start(TaskScheduler):实例方法 运行任务,可以传入任务调度器,或者采用默认线程池任务调度器。通过调用Start()方法执行的任务必须是 Task 或 Task<TResult> 类构造函数进行实例化。 要同步运行的任务必须处于 Created 状态。 任务只能启动并运行一次。 如果尝试再次计划任务,将导致异常。Task()创建的任务处于 Created状态,必须调用Start运行任务后任务状态才能变成WaitingtoRun。
//在当前线程运行任务
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
属性,所以指定有意义的名字可以很大程度的简化调试。
方法
Task.Delay延迟任务执行
内部用有一个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()
- Thread.Sleep()是同步延迟,Task.Delay()是异步延迟。
- Thread.Sleep()会阻塞线程,Task.Delay()不会。
- Thread.Sleep()不能取消,Task.Delay()可以。
- Task.Delay()实质创建一个运行给定时间的任务,Thread.Sleep()使当前线程休眠给定时间。
- 反编译Task.Delay(),基本上讲它就是个包裹在任务中的定时器。
- 4. Task.Delay() 比 Thread.Sleep() 消耗更多的资源,但是Task.Delay()可用于为方法返回Task类型;或者根据CancellationToken取消标记动态取消等待
- Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。
- 我的理解:Task.Delay(),async/await和CancellationTokenSource组合起来使用可以实现可控制的异步延迟
同步等待任务,会阻塞线程
wait
等待一个任务完成,都是实例方法
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();
WaitAll
等待一组任务完成,都是静态方法
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();
WaitAny
等等待一组任务中 任意一个完成 返回值是数组索引。都是静态方法
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();
异步等待任务,不会阻塞线程
WaitAsync
异步等待,实例方法,未提供int32类型Api但是可以用TimeSpan.FromMilliseconds(5000)来实现int32类型的等待。
Task WaitAsync(TimeSpan, CancellationToken) 获取一个任务,该任务将在此任务完成时、指定的超时超时时或指定的CancellationToken被请求取消时完成。
CancellationTokenSource CancellationTokenSource=new CancellationTokenSource();
CancellationTokenSource.Cancel();
Task<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(4000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; }); //异步等待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<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(4000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; }); //异步等待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任务完成后完成
WhenAll
等待一组任务完成,返回值是task,都是静态方法
static WhenAll(IEnumerable<Task>)
创建一个任务,该任务将在可枚举集合中的所有task对象都完成时完成。
调用WhenAll(IEnumerable<Task>方法不会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
static WhenAll(params
Task[])
创建一个任务,该任务将在数组中的所有task对象都完成时完成。
调用WhenAll(Task[])方法不会阻塞调用线程。
如果提供的任何任务在fault状态下完成,返回的任务也将在fault状态下完成,其中其异常将包含来自每个提供的任务的未包装异常集的聚合。
如果提供的任务中没有一个出错,但至少有一个被取消,则返回的任务将以取消状态结束。
如果没有任务出错,也没有任务被取消,则生成的任务将以RanToCompletion状态结束。
如果提供的数组/enumerable不包含任务,返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>>)
创建一个任务,该任务将在可枚举集合中的所有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 Task<TResult[]> WhenAll<TResult>(params
Task<TResult>[])
创建一个任务,该任务将在数组中的所有task 对象都完成时完成。
以上案例如下:
static WhenAll(IEnumerable<Task>)案例
var tasklist=new List<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完成"); }); 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<Task>方法不会阻塞调用线程。
如果提供的任何任务在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<TResult>(IEnumerable<Task<TResult>>)案例
var tasklist=new List<Task<string>>(); Task<string> task1 = Task<string>.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
Task<string> task2 = Task<string>.Run(() => { Console.WriteLine("任务2开始"); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet"; });
Task<string> task3 = Task<string>.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(10000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; }); tasklist.Add(task1);
tasklist.Add(task2);
tasklist.Add(task3); Task<string[]>taskswait = Task<string>.WhenAll<string>(tasklist);
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个元素的数组。
WhenAny
等待一组任务完成,返回值是Task<Task<TResult>>,Task<TResult>是第一返任务。都是静态方法
static Task<Task > WhenAny(IEnumerable<Task>)
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
static Task<Task > WhenAny(Task[])
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
static Task<Task > WhenAny<TResult>(Task<TResult>[])
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。
static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>>)
创建一个任务,该任务将在任何提供的任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“故障”状态结束,结果值也为真。
static Task<Task<TResult>> WhenAny(Task, Task)
创建一个任务,该任务将在两个任务中的任何一个任务完成时完成。
当提供的任何任务完成时,返回的任务将完成。返回的任务将总是以RanToCompletion状态结束,其Result被设置为第一个要完成的任务。即使要完成的第一个任务以“已取消”或“已故障”状态结束,也同样如此。
static Task<Task<TResult>> WhenAny<TResult>(Task<TResult>, Task<TResult>)案例
var tasklist=new List<Task<string>>(); Task<string> task1 = Task.Run(() => { Console.WriteLine("任务1开始"); Task.Delay(3000).Wait(); Console.WriteLine("任务1完成"); return "task1complet"; });
Task<string> task2 = Task.Run(() => { Console.WriteLine("任务2开始 准备返回的任务ID:"+ Task.CurrentId); Task.Delay(2000).Wait(); Console.WriteLine("任务2完成"); return "task2complet CurrentId"; });
Task<string> task3 = Task.Run(() => { Console.WriteLine("任务3开始") ; Task.Delay(4000).Wait(); Console.WriteLine("任务3完成"); return "task3complet"; }); tasklist.Add(task1);
tasklist.Add(task2);
tasklist.Add(task3); Task<Task<string>> taskswait = Task.WhenAny<string>(tasklist.ToArray());
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
}
Task.FromResult<TResult>(TResult)
创建一个返回值为TResult类值的Task实列。
Task<int> GetCustomerIdAsync()
{
return Task.FromResult(1123);
}
Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult());
//输出:1123
Task.FromException 方法
在通过task.Result和task.GetAwaiter().GetResult()获取该类实列的结果时,将触发异常。
Task<string> GetCustomerIdAsync()
{
return Task.FromException<string>(new OperationCanceledException("dfdfsdf"));
} try
{//将触发异常
string sdf= GetCustomerIdAsync().Result;
}
// Ignore exceptions here.
catch (AggregateException) { }
Console.WriteLine(GetCustomerIdAsync().GetAwaiter().GetResult() );//将触发异常
Task.FromCanceled 方法
返回一个带有取消标记的task实列。尚未对 cancellationToken
请求取消;其 IsCancellationRequested 属性为 false
。
CancellationTokenSource cts=new CancellationTokenSource();
Task<string> GetCustomerIdAsync()
{
return Task.FromCanceled<string>(cts.Token);
}
//要执行,
cts.Cancel(); Console.WriteLine(GetCustomerIdAsync().Status);//如果不执行cts.Cancel();将触发ArgumentOutOfRangeException异常
取消任务
可在创建Task时将一个CancellationToken传给构造器,从而将两者相关联,如果CancellationToken在Task调度前取消,那么Task就会被取消,永远都不执行。但如果Task已调度,那么Task的代码就只支持显示取消,其操作才能在执行期间取消,遗憾的是,虽然Task关联了一个CancellationToken,但却没有办法访问他。因此,必须在Task的代码中获得创建Task对象时的同一个CancellationToken。为此,最简单的办法就是使用一个Lamda表达式,将CancellationToken作为闭包变量传递。
使用同一个CancellationTokenSource取消多个任务
只需要多个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用来作用于一个异步任务的时候,你想达到其中一个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)
,紧随其后的代码将在当前上下文中继续在当前线程上执行。
Task在同一个线程运行的三种方式
//当前线程
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("当前上下文为空"); }
【C# Task】开篇的更多相关文章
- ABP理论学习之开篇介绍
返回总目录 为了和2016年春节赛跑,完成该系列博客,我牺牲了今天中午的时间来完成该系列的第一篇----开篇介绍.开篇介绍嘛,读过大学教材的同学都知道,这玩意总是那么无聊,跟考试没关系,干脆直接跳过, ...
- 微软BI 之SSIS 系列 - 使用 SQL Profilling Task (数据探测) 检测数据源数据
开篇介绍 SQL Profilling Task 可能我们很多人都没有在 SSIS 中真正使用过,所以对于这个控件的用法可能也不太了解.那我们换一个讲法,假设我们有这样的一个需求 - 需要对数据库表中 ...
- 大熊君大话NodeJS之开篇------Why NodeJS(将Javascript进行到底)
一,开篇分析 大家好啊,大熊君又来啦(*^__^*) 嘻嘻……,之前我写过一系列关于JS(OOP与设计模式)方面的文章,反响还好,其实这也是对我本人最大的鼓励,于是我决定我要将JavaScript进行 ...
- 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug
开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...
- 如何管理和记录 SSIS 各个 Task 的开始执行时间和结束时间以及 Task 中添加|删除|修改的记录数
开篇语 在这篇日志中 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架 我介绍到了包级别的日志管理框架,那么这个主要是针对包这一个层级的 Log 信息,包括包开始执行和结束时间,以 ...
- orleans开篇之hello world
orleans开篇之hello world 什么是orleans Orleans是一个建立在.NET之上的,设计的目标是为了方便程序员开发需要大规模扩展的云服务.Orleans项目基本上被认为是并行计 ...
- 微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件
原文:微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件 开篇介绍 大多数情况下我们的 SSIS 包都会配置在 SQL Agent ...
- 微软BI 之SSIS 系列 - 使用 Multicast Task 将数据同时写入多个目标表,以及写入Audit 与增量处理信息
开篇介绍 在 SSIS Data Flow 中有一个 Multicast 组件,它的作用和 Merge, Merge Join 或者 Union All 等合并数据流组件对比起来作用正好相反.非常直观 ...
- 微软BI 之SSIS 系列 - 理解Data Flow Task 中的同步与异步, 阻塞,半阻塞和全阻塞以及Buffer 缓存概念
开篇介绍 在 SSIS Dataflow 数据流中的组件可以分为 Synchronous 同步和 Asynchronous 异步这两种类型. 同步与异步 Synchronous and Asynchr ...
随机推荐
- 字符串自实现(一)(mystrcpy,mystrcat,mystrcmp)
char* mystrcpy(char* str_one,const char* str_two) { char* tmp = str_one; while (*str_one++ = *str_tw ...
- golang中GPM模型原理与调度器设计策略
一.GMP模型原理first: 1. 全局队列:存放待运行的G2. P的本地队列:同全局队列类似,存放待运行的G,存储的数量有限:256个,当创建新的G'时,G'优先加入到P的本地队列,如果队列已满, ...
- rsync实时备份监控命令(详细大全)
目录 一:rsync介绍 1.rsync简介 2.rsync特性 3.rsync应用场景 4.rsync的传输方式 5.Rsync传输模式 二:RSYNC使用参数 三:参数使用案例 一:rsync介绍 ...
- python删除列表重复元素
用list类的sort方法 l1 = ['b','c','d','b','c','a','a'] l2 = list(set(l1)) l2.sort(key=l1.index) print l2
- C# 读取txt文件生成Word文档
本文将以C#程序代码为例介绍如何来读取txt文件中的内容,生成Word文档.在编辑代码前,可参考如下代码环境进行配置: Visual Studio 2017 .Net Framework 4.6.1 ...
- python09day
内容回顾 文件操作初识 三步走: 打开文件open() 文件路径path,编码方式encoding=,mode(默认读) 操作文件(对文件句柄进行操作) 读.写.追加 各四种模式 读:read().r ...
- Windows安装软件出现 2502 2503的错误?
1 输入这个命令 2 3 msiexec /package +"需要安装文件的路径" 4 5 //注意路径的问题 斜杆要保持一致. 6 //注意要有空格. 我的安装路径 7 msi ...
- Maven的安装、配置与使用
5.Maven 我为什么要学习这个技术? 在JavaWeb开发中,需要使用大量的jar包,我们手动去导入: 如何能够让一个东西自动帮我们导入和配置这个jar包. 由此,Maven诞生! 5.1.Mav ...
- 元数据性能大比拼:HDFS vs OSS vs JuiceFS
背景 存储是大数据的基石,存储系统的元数据又是它的核心大脑,元数据的性能对整个大数据平台的性能和扩展能力非常关键.本文选取了大数据平台中 3 个典型的存储方案来压测元数据的性能,来个大比拼. 其中 H ...
- Uwl.Admin.Core开源框架(二) 使用QuartzNet
Uwl.Admin.Core中使用QuartzNet定时任务模块: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Repos ...