1. Tâche de tâche
System.Threading.Tasks a été introduit dans .NET4, et il y en a trop. API pour les threads précédents Il n'est pas pratique à contrôler et la capacité de contrôle de ThreadPool est trop faible. Par exemple, il n'est pas pratique d'effectuer la continuation, le blocage, l'annulation, le délai d'attente et d'autres fonctions du thread, donc Task fait abstraction de la fonction du thread et utilise ThreadPool.
1. Démarrez la tâche
Vous pouvez utiliser la classe TaskFactory ou le constructeur et la méthode Start() de la classe Task. un paramètre d'entrée avec un type Object, afin que vous puissiez transmettre n'importe quelle donnée à la tâche. , et vous avez également manqué un Task.RunTaskFactory taskFactory = new TaskFactory(); taskFactory.StartNew(() => { Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); Task.Factory.StartNew(() => { Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); Task task = new Task(() => { Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); task.Start();
Task task = new Task(() => { Thread.Sleep(10000); Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); task.RunSynchronously();
2. Suite du blocage
Dans Thread, nous utilisons join pour bloquer l'attente. Il n'est pas pratique de contrôler plusieurs Threads. Dans Task, nous utilisons la méthode d'instance Wait pour bloquer une seule tâche ou les méthodes statiques WaitAll et WaitAny pour bloquer plusieurs tâchesvar task = new Task(() => { Thread.Sleep(5*1000); Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); var task2 = new Task(() => { Thread.Sleep(10 * 1000); Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); task.Start(); task2.Start(); //task.Wait();//单任务等待 //Task.WaitAny(task, task2);//任何一个任务完成就继续 Task.WaitAll(task, task2);//任务都完成才继续
Task.WhenAll(task, task2).ContinueWith((t) => { Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); Task.Factory工厂中也存在类似ContinueWhenAll和ContinueWhenAny
3. Hiérarchie des tâches
Vous pouvez non seulement exécuter une autre tâche après la fin de une tâche, mais vous pouvez également en démarrer une dans une tâche Task, cela démarre une hiérarchie parent-enfantvar parentTask = new Task(()=> { Console.WriteLine($"parentId={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); Thread.Sleep(5*1000); var childTask = new Task(() => { Thread.Sleep(10 * 1000); Console.WriteLine($"childId={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}") }); childTask.Start(); }); parentTask.Start();
4.
Une brève introduction à la création de tâches Le paramètre d'énumération TaskCreationOptions, lors de la création d'une tâche, nous pouvons fournir le paramètre d'énumération TaskCreationOptions, un indicateur utilisé pour contrôler le comportement facultatif de création et d'exécution de tâches AttachedToParent : Spécifie d'attacher la tâche à la hiérarchie des tâches. Un certain parent signifie établir une relation parent-enfant. La tâche parent doit attendre que la tâche enfant soit terminée avant de pouvoir continuer à s'exécuter. L'effet est le même que celui de WaitAll. Dans l'exemple ci-dessus, si TaskCreationOptions.AttachedToParent est spécifié lors de la création d'une sous-tâche, la tâche parent attendra également la fin de la sous-tâche en attendant DenyChildAttach : Ne pas autoriser l'attachement de la sous-tâche à la tâche parent LongRunning : Spécifiez une tâche de longue durée. Si vous savez à l'avance que la tâche prendra beaucoup de temps, il est recommandé de la définir. De cette façon, le planificateur de tâches créera des threads de thread au lieu d'utiliser des threads ThreadPool. Parce que vous occupez le thread ThreadPool pendant une longue période et ne le retournez pas, il peut ouvrir un nouveau thread dans le pool de threads si nécessaire, provoquant une pression de planification PreferFairness : organiser les tâches aussi équitablement que possible, ce qui signifie que les tâches planifiées plus tôt seront plus susceptibles de s'exécuter plus tôt, et les tâches planifiées pour s'exécuter plus tard seront plus susceptibles de s'exécuter plus tard. En fait, la tâche est placée dans la file d'attente globale du pool de threads et laisse les threads de travail en compétition pour elle. La file d'attente par défaut est dans la file d'attente locale. Un autre paramètre d'énumération est le paramètre d'énumération TaskContinuationOptions dans la méthode ContinueWith En plus d'avoir plusieurs valeurs d'énumération avec les mêmes fonctions que ci-dessus, il possède également des fonctions telles que le contrôle de l'annulation et de la poursuite des tâches <🎜. >LazyCancellation : En cas d'annulation de continuation, empêche l'achèvement de la continuation jusqu'à ce que la tâche précédente soit terminée. Qu'est-ce que ça veut dire?
CancellationTokenSource source = new CancellationTokenSource(); source.Cancel(); var task1 = new Task(() => { Console.WriteLine($"task1 id={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); var task2 = task1.ContinueWith(t => { Console.WriteLine($"task2 id={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); },source.Token); var task3 = task2.ContinueWith(t => { Console.WriteLine($"task3 id={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); }); task1.Start();
Dans l'exemple ci-dessus, nous essayons d'exécuter la tâche1->task2->task3 séquentiellement, puis annulons l'exécution de la tâche2 via CancellationToken. Quel sera le résultat ? En conséquence, la tâche 1 et la tâche 3 seront exécutées en parallèle (la tâche 3 sera également exécutée, et en parallèle avec la tâche 1, ce qui signifie que la chaîne d'origine devient deux chaînes), puis on essaie d'utiliser
LazyCancellation, var task2 = task1.ContinueWith(t => { Console.WriteLine($"task2 id={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); },source.Token,TaskContinuationOptions.LazyCancellation,TaskScheduler.Current);
donc qu'il sera dans task1 Une fois l'exécution terminée, task2 jugera la source.Token Si c'est Cancel, il ne sera pas exécuté la prochaine exécution de task3 garantira l'ordre d'origine
ExecuteSynchronously : Spécifie. que la tâche de continuation doit être exécutée de manière synchrone. Par exemple, dans l'exemple ci-dessus, dans la continuation Si ce paramètre est spécifié dans task2, task2 sera exécuté en utilisant le thread qui exécute la tâche1. Cela empêche le changement de thread et permet un certain accès aux ressources partagées. . Si non précisé, elle sera aléatoire, mais le thread de la tâche1 peut également être utilisé
NotOnRanToCompletion : La tâche de continuation doit être exécutée dans l'état de non-achèvement de la tâche précédente
OnlyOnRanToCompletion : La tâche de continuation doit être dans l'état d'achèvement de la tâche précédente Pour exécuter
NotOnFaulted, OnlyOnCanceled, OnlyOnFaulted, etc.
5. Annulation de la tâcheLors de l'utilisation de Thread dans l'article précédent, nous avons utilisé une variable isStop mark. Que ce soit pour annuler la tâche ou non, cette méthode d'accès aux variables partagées posera inévitablement des problèmes. La classe CancellationTokenSource est proposée dans task pour gérer spécifiquement l'annulation de tâche. Pour une utilisation courante, voir les commentaires de code ci-dessous
CancellationTokenSource source = new CancellationTokenSource();//构造函数中也可指定延迟取消 //注册一个取消时调用的委托 source.Token.Register(() => { Console.WriteLine("当前source已经取消,可以在这里做一些其他事情(比如资源清理)..."); }); var task1 = new Task(() => { while (!source.IsCancellationRequested) { Console.WriteLine($"task1 id={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"); } },source.Token); task1.Start(); //source.Cancel();//取消 source.CancelAfter(1000);//延时取消
. 让子线程返回结果,可以将信息写入到线程安全的共享变量中去,或则使用可以返回结果的任务。使用Task的泛型版本Task 7、异常 可以使用AggregateException来接受任务中的异常信息,这是一个聚合异常继承自Exception,可以遍历获取包含的所有异常,以及进行异常处理,决定是否继续往上抛异常等 二、并行Parallel 1、Parallel.For()、Parallel.ForEach() 在.NET4中,另一个新增的抽象的线程时Parallel类。这个类定义了并行的for和foreach的静态方法。Parallel.For()和Parallel.ForEach()方法多次调用一个方法,而Parallel.Invoke()方法允许同时调用不同的方法。首先Parallel是会阻塞主线程的,它将让主线程也参与到任务中 Parallel.For()类似于for允许语句,并行迭代同一个方法,迭代顺序没有保证的 也可以提前中断Parallel.For()方法。For()方法的一个重载版本接受Action 2、Parallel.For For还有一个高级泛型版本,相当于并行的聚合计算 像下面这样我们求0…100的和,第三个参数更定一个种子初始值,第四个参数迭代累计,最后聚合 上面For用来处理数组数据,ForEach()方法用来处理非数组的数据任务,比如字典数据继承自IEnumerable的集合等 3、Parallel.Invoke() Parallel.Invoke()则可以并行调用不同的方法,参数传递一个Action的委托数组 4、PLinq Plinq,为了能够达到最大的灵活度,linq有了并行版本。使用也很简单,只需要将原始集合AsParallel就转换为支持并行化的查询。也可以AsOrdered来顺序执行,取消Token,强制并行等 三、异步等待AsyncAwait 异步编程模型,可能还需要大篇幅来学习,这里先介绍下基本用法,内在本质需要用ILSpy反编译来看,以后可能要分专题总结。文末先给几个参考资料,有兴趣自己阔以先琢磨琢磨鸭 1、简单使用 这是.NET4.5开始提供的一对语法糖,使得可以较简便的使用异步编程。async用在方法定义前面,await只能写在带有async标记的方法中,任何方法都可以增加async,一般成对出现,只有async没有意义,只有await会报错,请先看下面的示例 一般使用async都会让方法返回一个Task的,像下面这样复杂一点的 通过var reslult = AsyncTest2().Result;调用即可。但注意如果调用Wait或Result的代码位于UI线程,Task的实际执行在其他线程,其需要返回UI线程则会造成死锁,所以应该Async all the way 2、优雅 从上面简单示例中可以看出异步编程的执行逻辑:主线程A逻辑->异步任务线程B逻辑->主线程C逻辑。 异步方法的返回类型只能是void、Task、Task。示例中异步方法的返回值类型是Task,通常void也不推荐使用,没有返回值直接用Task就是 上一篇也大概了解到如果我们要在任务中更新UI,需要调用Invoke通知UI线程来更新,代码看起来像下面这样,在一个任务后去更新UI 如果使用async/await会看起来像这样,是不是优雅了许多。以看似同步编程的方式实现异步var task = new Task<string>(() =>
{
return "hello ketty";
});
task.Start();
string result = task.Result;
var task = Task.Factory.StartNew(() =>
{
var childTask1 = Task.Factory.StartNew(() =>
{
throw new Exception("childTask1异常...");
},TaskCreationOptions.AttachedToParent);
var childTask12= Task.Factory.StartNew(() =>
{
throw new Exception("childTask2异常...");
}, TaskCreationOptions.AttachedToParent);
});
try
{
try
{
task.Wait();
}
catch (AggregateException ex)
{
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine($"message{item.InnerException.Message}");
}
ex.Handle(x =>
{
if (x.InnerException.Message == "childTask1异常...")
{
return true;//异常被处理,不继续往上抛了
}
return false;
});
}
}
catch (Exception ex)
{
throw;
}
ParallelLoopResult result = Parallel.For(0, 10, i =>
{
Console.WriteLine($"{i} task:{Task.CurrentId} thread:{Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine(result.IsCompleted);
ParallelLoopResult result = Parallel.For(0, 10, new ParallelOptions() { MaxDegreeOfParallelism = 8 },(i,loop) =>
{
Console.WriteLine($"{i} task:{Task.CurrentId} thread:{Thread.CurrentThread.ManagedThreadId}");
if (i > 5)
{
loop.Break();
}
});
ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally);
int totalNum = 0;
Parallel.For<int>(0, 100, () => { return 0; }, (current, loop, total) =>
{
total += current;
return total;
}, (total) =>
{
Interlocked.Add(ref totalNum, total);
});
Parallel.Invoke(() => { Console.WriteLine($"方法1 thread:{Thread.CurrentThread.ManagedThreadId}"); }
, () => { Console.WriteLine($"方法2 thread:{Thread.CurrentThread.ManagedThreadId}"); }
, () => { Console.WriteLine($"方法3 thread:{Thread.CurrentThread.ManagedThreadId}"); });
var nums = Enumerable.Range(0, 100);
var query = from n in nums.AsParallel()
select new
{
thread=$"tid={Thread.CurrentThread.ManagedThreadId},datetime={DateTime.Now}"
};
private static async void AsyncTest()
{
//主线程执行
Console.WriteLine($"before await ThreadId={Thread.CurrentThread.ManagedThreadId}");
TaskFactory taskFactory = new TaskFactory();
Task task = taskFactory.StartNew(() =>
{
Thread.Sleep(3000);
Console.WriteLine($"task ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
await task;//主线程到这里就返回了,执行主线程任务
//子线程执行,其实是封装成委托,在task之后成为回调(编译器功能 状态机实现) 后面相当于task.ContinueWith()
//这个回调的线程是不确定的:可能是主线程 可能是子线程 也可能是其他线程,在winform中是主线程
Console.WriteLine($"after await ThreadId={Thread.CurrentThread.ManagedThreadId}");
}
private static async Task<string> AsyncTest2()
{
Console.WriteLine($"before await ThreadId={Thread.CurrentThread.ManagedThreadId}");
TaskFactory taskFactory = new TaskFactory();
string x = await taskFactory.StartNew(() =>
{
Thread.Sleep(3000);
Console.WriteLine($"task ThreadId={Thread.CurrentThread.ManagedThreadId}");
return "task over";
});
Console.WriteLine($"after await ThreadId={Thread.CurrentThread.ManagedThreadId}");
return x;
}
private void button1_Click(object sender, EventArgs e)
{
var ResultTask = Task.Run(() => {
Thread.Sleep(5000);
return "任务完成";
});
ResultTask.ContinueWith((r)=>
{
textBox1.Invoke(() => {
textBox1.Text = r.Result;
});
});
}
private async void button1_Click(object sender, EventArgs e)
{
var t = Task.Run(() => {
Thread.Sleep(5000);
return "任务完成";
});
textBox1.Text = await t;
}
3、最后
在.NET 4.5中引入的Async和Await两个新的关键字后,用户能以一种简洁直观的方式实现异步编程。甚至都不需要改变代码的逻辑结构,就能将原来的同步函数改造为异步函数。
在内部实现上,Async和Await这两个关键字由编译器转换为状态机,通过System.Threading.Tasks中的并行类实现代码的异步执行。
本文来自 C#.Net教程 栏目,欢迎学习!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!