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。

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状态。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习C#多线程的路线可以按照以下步骤进行: 1. 先了解基本概念:开始学习之前,建议先了解多线程的基本概念和原理,包括线程、进程、并发、同步等相关概念。 2. 学习线程的创建和启动:学习如何在C#中创建和启动线程,可以使用Thread类或者Task类来实现。 3. 线程同步和互斥:学习如何在多线程环境下实现线程同步和互斥,以避免出现竞态条件和数据不一致的问题。可以学习使用锁、互斥量、信号量等机制来实现线程同步。 4. 学习线程间通信:学习如何在多线程环境下实现线程间的通信,以便线程之间能够进行数据交换和协作。可以学习使用共享内存、消息队列、事件等机制来实现线程间通信。 5. 学习线程池:学习如何使用线程池来管理和调度线程,以提高多线程应用程序的性能和效率。 6. 学习并发集合:学习如何使用并发集合来处理多线程环境下的数据共享和访问问题,包括并发队列、并发字典、并发栈等。 7. 学习异步编程:学习如何使用异步编程模型(Async/Await)来实现高效的异步操作,以提高多线程应用程序的响应性和吞吐量。 8. 学习线程安全性:学习如何编写线程安全的代码,以避免出现竞态条件和数据不一致的问题。可以学习使用锁、原子操作、线程本地存储等技术来确保线程安全性。 9. 实践项目:通过实践项目来巩固所学的多线程知识,可以选择一些具有多线程需求的项目来进行实践,例如网络服务器、并发任务处理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值