C#4.0的并行库TPL,即Task(二)

C#4.0的并行库TPL,即Task(一)http://www.skcircle.com/?id=1793

C#4.0的并行库TPL,即Task(二) http://www.skcircle.com/?id=1798

C#4.0的并行库TPL,即Task(三) http://www.skcircle.com/?id=1808

C#4.0的并行库TPL,即Task(四)  http://www.skcircle.com/?id=1815

C#4.0的并行库TPL,即Task(五) http://www.skcircle.com/?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;
		}
	}
}

image.png




示例七:TaskScheduler进行调度Task


程序启动后,有三个按钮。

第一按钮Sync调用一个同步任务的执行。这时候窗体卡住。


第二个按钮Async在另一个线程中访问UI(异步运行任务), 窗体是没有卡住,但是5秒钟后报一个错误。如图7.2所示。

这是因为ui被定义为不允许从UI线程之外的线程进行访问。

如果我们强行继续,会看到如图7.3的信息。

而之所以你可以看到继续输出的错误信息,是因为TaskScheduler.FromCurrentSynchronizationContext()这个方法。

它使TPL基础设施对UI的操作转到UI线程上去操作。


第三个按钮Async OK,依图7.4的结果,我们可以看到异步代码工作在线程Id为8的线程上面,它就是UI线程。


image.png

(图7.1)


image.png

(图7.2)


image.png

(图7.3)


image.png

(图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());

        }
    }
}


最后来补充一个经典的死锁案例,将上例中的代码增加一行Result:


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

本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864