C#多线程学习笔记五

Task构造函数中使用的枚举

public Task(Action action, TaskCreationOptions creationOptions);
public enum TaskCreationOptions
    {
    
    
        None = 0,
        PreferFairness = 1,
        LongRunning = 2,
        AttachedToParent = 4,
        DenyChildAttach = 8,
        HideScheduler = 16,
        RunContinuationsAsynchronously = 64
}

AttachedToParent

指定将任务附加到任务层次结构中的某个父级。
建立了父子关系,父任务想要继续执行,必须等待子任务执行完毕。其实等价于一个WaitAll操作

Task task = new Task(()=> 
{
    
    
Task task1 = new Task(() =>
{
    
    
Thread.Sleep(100);
        Console.WriteLine("t1");
     }, TaskCreationOptions.AttachedToParent);
Task task2 = new Task(() =>
{
    
    
Thread.Sleep(10);
        Console.WriteLine("t2");
     }, TaskCreationOptions.AttachedToParent);
task1.Start();
    task2.Start();
});
task.Start();
task.Wait();
Console.WriteLine("main");

输出顺序为t2 t1 main。此时task.Wait()等同于Task.WaitAll(task1,task2)。
如果去了枚举TaskCreationOptions.AttachedToParent,则输出结果为main t2 t1。
由此可以看出,AttachedToParent枚举标志的作用是让父线程等待其子线程执行完毕。

DenyChildAttach

不让子任务附加到父任务

Task task = new Task(()=> 
{
    
    
Task task1 = new Task(() =>
{
    
    
Thread.Sleep(100);
        Console.WriteLine("t1");
     }, TaskCreationOptions.AttachedToParent);
Task task2 = new Task(() =>
{
    
    
Thread.Sleep(10);
        Console.WriteLine("t2");
     }, TaskCreationOptions.AttachedToParent);
task1.Start();
    task2.Start();
},TaskCreationOptions.DenyChildAttach);
task.Start();
task.Wait();
Console.WriteLine("main");

输出顺序为:main t2 t1。因为最外层的父任务添加了DenyChildAttach标志。

HideScheduler

子任务默认不使用父类Task的Schedule,而是使用默认的

LongRunning

如果是长时间运行的任务,建议添加此选项。
同时,建议使用Thread,而不是ThreadPool,如果长期借用ThreadPool里的线程而不还,线程池为了满足需求会新开一些线程,这会导致ThreadPool的线程过多,销毁和调度都是一个很大的麻烦。

PreferFairness

提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运

任务延续中使用的枚举

public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
public enum TaskContinuationOptions
{
    
    
   None = 0,
   PreferFairness = 1,
   LongRunning = 2,
   AttachedToParent = 4,
   DenyChildAttach = 8,
   HideScheduler = 16,
   LazyCancellation = 32,
   RunContinuationsAsynchronously = 64,
   NotOnRanToCompletion = 65536,
   NotOnFaulted = 131072,
   OnlyOnCanceled = 196608,
   NotOnCanceled = 262144,
   OnlyOnFaulted = 327680,
   OnlyOnRanToCompletion = 393216,
   ExecuteSynchronously = 524288
}

其中有一部分枚举和构造函数中的枚举一样,用法也一样。

LazyCancellation

CancellationTokenSource source = new CancellationTokenSource();
source.Cancel();
Task task1 = new Task(() => 
{
    
    
    Thread.Sleep(1000);
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
var task2 = task1.ContinueWith(t => 
{
    
    
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
},source.Token ,TaskContinuationOptions.LazyCancellation,TaskScheduler.Current);
var task3 = task2.ContinueWith(t =>
{
    
    
Console.WriteLine("task3,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
task1.Start();

输出顺序:task1 task3
在task2后的ContinueWith中加入TaskContinuationOptions.LazyCancellation会在task2被取消时,依然保持ContinueWith关系,等task1执行完才判断source.Token和task2是否被取消,使得task1 -> ask2 -> task3链不断,在执行完task1后再执行task1。

扫描二维码关注公众号,回复: 13596558 查看本文章

ExecuteSynchronously

指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。 如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。
通俗的说,这个枚举就是希望执行前面task的线程也来执行本延续任务。task2也希望使用task1的线程去执行,它可以防止线程切换即时间片切换。

Task task1 = new Task(() => 
{
    
    
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
});
var task2 = task1.ContinueWith(t => 
{
    
    
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions.ExecuteSynchronously);

执行结果:task1和task2均使用同一线程,tid的结果相同。

NotOnRanToCompletion、OnlyOnRanToCompletion

前面一个标志表示延续任务必须在前面task非完成状态才能执行
后面一个标志表示延续任务必须在前面task完成状态才能执行

Task task1 = new Task(() => 
{
    
    
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
throw new Exception("hello word");
});
var task2 = task1.ContinueWith(t => 
{
    
    
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions. OnlyOnRanToCompletion);

加入OnlyOnRanToCompletion后,由于task1抛出异常,所以task2不执行。

NotOnFaulted、OnlyOnFaulted

前面一个标志表示延续任务必须在前面task非失败状态才能执行
后面一个标志表示延续任务必须在前面task失败状态才能执行

Task task1 = new Task(() => 
{
    
    
Console.WriteLine("task1,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
throw new Exception("hello word");
});
var task2 = task1.ContinueWith(t => 
{
    
    
Console.WriteLine("task2,tid={0},dt={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}, TaskContinuationOptions. OnlyOnFaulted);

加入OnlyOnFaulted后,虽然task1抛出异常,但是task2依然接着执行且只在task1失败抛出异常时才执行。

NotOnCanceled、OnlyOnCanceled

前面一个标志表示延续任务必须在前面task没被取消状态才能执行
后面一个标志表示延续任务必须在前面task被取消状态才能执行

总结

如果某些任务被取消,它既不是错误也不是完成,所以他不会被OnlyOnFaulted处理,但是会被NotOnRanToCompletion会被处理。
所以:
NotOnRanToCompletion | NotOnFaulted == OnlyOnCancelled
NotOnCanceled | NotOnFaulted == OnlyOnRanToCompletion
NotOnRanToCompletion | NotOnCanceld == OnlyOnFaulted

用这些枚举标记的好处是不用判断task的isComplete、isFaulted、isCanceled状态。

猜你喜欢

转载自blog.csdn.net/Z960515/article/details/113437586