C#4.0的并行库TPL,即Task(一)http://47.98.154.65/?id=1793
C#4.0的并行库TPL,即Task(二) http://47.98.154.65/?id=1798
C#4.0的并行库TPL,即Task(三) http://47.98.154.65/?id=1808
C#4.0的并行库TPL,即Task(四) http://47.98.154.65/?id=1815
C#4.0的并行库TPL,即Task(五) http://47.98.154.65/?id=1816
勇哥继续上节关于Task的话题。
示例六:并行运行任务
程序启动后创建了两个task。然后用Task.WhenAll方法,创建了第三个Task,
该任务会在所得分任务完成后运行,该任务的结果是一个数组,元素1表示第一个任务结果,
第二个元素表示第二个任务的结果,以此类推。
后面用for循环创建了一系列任务,并使用Task.WhenAny方法等待这些任务中的任何一个完成。
当得分一个完成任务后,从列表中移除该任务并继续等待其它任务完成,直到列表为空。
获取任务完成情况或者判断运动中的任务是否超时,都可以用Task.WhenAny方法。
例如:一组任务判断超时,我们使用其中一个任务来记录是否超时,如果该任务先完成,则只需要取消掉其它还未完成的任务。
代码:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Chapter4.Recipe8 { class Program { static void Main(string[] args) { var firstTask = new Task<int>(() => TaskMethod("First Task", 3)); var secondTask = new Task<int>(() => TaskMethod("Second Task", 2)); var whenAllTask = Task.WhenAll(firstTask, secondTask); whenAllTask.ContinueWith(t => Console.WriteLine("The first answer is {0}, the second is {1}", t.Result[0], t.Result[1]), TaskContinuationOptions.OnlyOnRanToCompletion ); firstTask.Start(); secondTask.Start(); Thread.Sleep(TimeSpan.FromSeconds(4)); var tasks = new List<Task<int>>(); for (int i = 1; i < 4; i++) { int counter = i; var task = new Task<int>(() => TaskMethod(string.Format("Task {0}", counter), counter)); tasks.Add(task); task.Start(); } while (tasks.Count > 0) { var completedTask = Task.WhenAny(tasks).Result; tasks.Remove(completedTask); Console.WriteLine("A task has been completed with result {0}.", completedTask.Result); } Thread.Sleep(TimeSpan.FromSeconds(1)); Console.ReadKey(); } static int TaskMethod(string name, int seconds) { Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds)); return 42 * seconds; } } }
示例七:TaskScheduler进行调度Task
程序启动后,有三个按钮。
第一按钮Sync调用一个同步任务的执行。这时候窗体卡住。
第二个按钮Async在另一个线程中访问UI(异步运行任务), 窗体是没有卡住,但是5秒钟后报一个错误。如图7.2所示。
这是因为ui被定义为不允许从UI线程之外的线程进行访问。
如果我们强行继续,会看到如图7.3的信息。
而之所以你可以看到继续输出的错误信息,是因为TaskScheduler.FromCurrentSynchronizationContext()这个方法。
它使TPL基础设施对UI的操作转到UI线程上去操作。
第三个按钮Async OK,依图7.4的结果,我们可以看到异步代码工作在线程Id为8的线程上面,它就是UI线程。
(图7.1)
(图7.2)
(图7.3)
(图7.4)
代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication5 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } Task<string> TaskMethod(TaskScheduler scheduler) { Task delay = Task.Delay(5000); return delay.ContinueWith(t => { string str = string.Format("Task is running on a thread id {0}. Is thread pool thread: {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); ContentTextBlock.Text = str; return str; }, scheduler); } private void button1_Click(object sender, EventArgs e) { ContentTextBlock.Text = string.Empty; try { string result = TaskMethod(TaskScheduler.Default).Result; ContentTextBlock.Text = result; } catch (Exception ex) { ContentTextBlock.Text = ex.InnerException.Message; } } private void button3_Click(object sender, EventArgs e) { label3.Text =$"UI线程:{ Environment.CurrentManagedThreadId.ToString()}"; label2.Text = string.Empty; // task 并没有运行在线程池中,而是 FromCurrentSynchronizationContext Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()); task.ContinueWith(t => { label2.Text = "hello!"; }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } private void button2_Click(object sender, EventArgs e) { ContentTextBlock.Text = string.Empty; //这个函数主要就是这里用了default,所以用了线程池线程,而taskmethod里面有操作ui对象,因此出错! Task<string> task = TaskMethod(TaskScheduler.Default); task.ContinueWith(t => { //这里的后续操作是在ui线程中做的,没有出错。 ContentTextBlock.Text = t.Exception.InnerException.Message; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, //onlyonfaulted, TaskScheduler.FromCurrentSynchronizationContext()); } } }
string s = task.Result; //这句话将让UI线程等待直到UI线程完成task中的内容,
但是等待中的UI线程没有办法操作,因此死锁!
private void button3_Click(object sender, EventArgs e) { label3.Text =$"UI线程:{ Environment.CurrentManagedThreadId.ToString()}"; label2.Text = string.Empty; // task 并没有运行在线程池中,而是 FromCurrentSynchronizationContext Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()); //这句话将让UI线程等待直到UI线程完成task中的内容,但是等待中的UI线程没有办法操作,因此死锁! string s = task.Result; task.ContinueWith(t => { label2.Text = "hello!"; }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); }
为了避免死锁,绝对不要通过任务调度程序在UI线程中使用同步操作,请使用C# 5.0中的 ContinueWith或者async/await方法。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

