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
版权声明:本文为博主原创文章,转载请附上博文链接!


少有人走的路


















