TaskCreationOptions的使用与了解
TaskCreationOptions是Task任务的一个枚举,当Task任务长时间运行时,建议在创建任务时,使用TaskCreationOptions.LongRunning
长时间的任务在ThreadPool中,如果长时间不归还线程,系统会强制开启一个新的线程,会一定程度的影响程序的性能
在使用TaskCreationOptions.AttachedToParent时,其作用相当于Task.WaitAll
//
// 摘要:
// 指定用于控制任务的创建和执行的可选行为的标志。
[Flags]
public enum TaskCreationOptions
{
//
// 摘要:
// 指定应使用默认行为。
None = 0,
//
// 摘要:
// 提示 System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
PreferFairness = 1,
//
// 摘要:
// 指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 System.Threading.Tasks.TaskScheduler
// 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。
LongRunning = 2,
//
// 摘要:
// 指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 System.Threading.Tasks.TaskContinuationOptions.AttachedToParent
// 选项以便将父任务和子任务同步。 请注意,如果使用 System.Threading.Tasks.TaskCreationOptions.DenyChildAttach
// 选项配置父任务,则子任务中的 System.Threading.Tasks.TaskCreationOptions.AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。
// 有关详细信息,请参阅附加和分离的子任务。
AttachedToParent = 4,
//
// 摘要:
// 指定任何尝试作为附加的子任务执行(即,使用 System.Threading.Tasks.TaskCreationOptions.AttachedToParent
// 选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务。
DenyChildAttach = 8,
//
// 摘要:
// 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default
// 当前计划程序。
HideScheduler = 16,
//
// 摘要:
// 强制异步执行添加到当前任务的延续任务。 请注意,System.Threading.Tasks.TaskCreationOptions.RunContinuationsAsynchronously
// 成员在以 .NET Framework 4.6 开头的 System.Threading.Tasks.TaskCreationOptions 枚举中可用。
RunContinuationsAsynchronously = 64
}
public static void Test8()
{
Task partentTask = Task.Factory.StartNew(() =>
{
Task childTask1 = new Task(() =>
{
Thread.Sleep(1000);
//业务逻辑
Console.WriteLine("childTask1线程ID:" + Thread.CurrentThread.ManagedThreadId + " " + "Time:" + DateTime.Now.ToString());
},TaskCreationOptions.AttachedToParent);
Task childTask2 = new Task(() =>
{
Thread.Sleep(3000);
//业务逻辑
Console.WriteLine("childTask2线程ID:" + Thread.CurrentThread.ManagedThreadId + " " + "Time:" + DateTime.Now.ToString());
},TaskCreationOptions.AttachedToParent);
childTask1.Start();
childTask2.Start();
});
partentTask.Wait();//相当于waitALL,等待所有子任务执行完成后执行其他任务
//业务逻辑
//TaskCreationOptions.AttachedToParent如果这个枚举参数不添加,主线程会直接运行,不等待
Console.WriteLine("partentTask线程ID:" + Thread.CurrentThread.ManagedThreadId + " " + "Time:" + DateTime.Now.ToString());
}
//如果任务是长时间运行的,建议加上TaskCreationOptions.LongRunning,ThreadPool如果长时间不归还线程,系统会强制开启新的线程,会影响程序的性能。
Task任务的取消(CancellationTokenSource)
当Task任务执行取消,希望程序可以执行一些其他的任务,这时候可以通过CancellationTokenSource实例对象的Token.Resister方法来执行
应用场景:数据库连接超时自动取消,TCP自动重连等等
public static void Test10()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task task =new Task(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
Console.WriteLine("Task线程id:"+Thread.CurrentThread.ManagedThreadId+" "+"Time:"+DateTime.Now.ToString());
}
Console.WriteLine("Task线程id:"+Thread.CurrentThread.ManagedThreadId+" " +"Time:"+DateTime.Now.ToString());
},cts.Token);
task.Start();
cts.Token.Register(() =>
{
Console.WriteLine("正在执行清理。。。。");
Thread.Sleep(2000);
Console.WriteLine("清理完成");
});
Thread.Sleep(3000);
cts.Cancel();//当cancel方法被执行了,会转向Token.Register方法的业务逻辑
}
//Task任务延时自动取消:比如我们请求一个远程接口,如果长时间没有返回数据,我们可以做一个时间限制,超时可以取消任务(比如微信红包退回)
使用cts.CancelAfter(3000);//任务3s后自动取消,可以设置多少秒后自动取消
线程锁
在多线程中,必定会存在资源的竞争,使用线程锁可以使线程有效有序的访问资源
1、Lock是monitor的语法糖,本质上是解决资源的竞争,和资源锁定的问题
2、Lock锁住的资源不能是局部变量,要能被其他线程访问到
3、Lock不能锁住string类型
多线程还要其他的线程锁,这里简单描述一下Lock
没有使用线程锁的情况,资源无序访问
private static object mylock=new object();
static int num = 0;
public static void Test15()
{
for(int i = 0; i < 5; i++)
{
Task task = Task.Factory.StartNew(() =>
{
test34();
});
}
}
public static void test34()
{
for(int i = 0; i < 100; i++)
{
lock (mylock)
{
num++;
Console.WriteLine(num);
}
}
}