精华内容
下载资源
问答
  • task
    千次阅读 多人点赞
    2021-02-20 14:49:40

    C# Task详解

    1、Task的优势?

    hreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
      ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
      ◆ ThreadPool不支持线程执行的先后次序;
      以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式。
      以下是一个简单的任务示例:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Task t = new Task(() =>
                {
                    Console.WriteLine("任务开始工作……");
                    //模拟工作过程
                    Thread.Sleep(5000);
                });
                t.Start();
                t.ContinueWith((task) =>
                {
                    Console.WriteLine("任务完成,完成时候的状态为:");
                    Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                });
                Console.ReadKey();
            }
        }
    }
    

    2、Task的用法

    2-1.无返回值的方式

    方式1代码如下(示例):

    	var t1 = new Task(() => TaskMethod("Task 1"));
      t1.Start();
      Task.WaitAll(t1);//等待所有任务结束 
      注:任务的状态:
      Start之前为:Created
      Start之后为:WaitingToRun 
    

    方式2代码如下(示例):

    	Task.Run(() => TaskMethod("Task 2"));
    

    方式3代码如下(示例):

    	Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接异步的方法 
      //或者
      var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
      Task.WaitAll(t3);//等待所有任务结束
      //任务的状态:
      Start之前为:Running
      Start之后为:Running
    

    主程序1(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var t1 = new Task(() => TaskMethod("Task 1"));
                var t2 = new Task(() => TaskMethod("Task 2"));
                t2.Start();
                t1.Start();
                Task.WaitAll(t1, t2);
                Task.Run(() => TaskMethod("Task 3"));
                Task.Factory.StartNew(() => TaskMethod("Task 4"));
                //标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行。
                Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
    
                #region 常规的使用方式
                Console.WriteLine("主线程执行业务处理.");
                //创建任务
                Task task = new Task(() =>
                {
                    Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine(i);
                    }
                });
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                task.Start();
                Console.WriteLine("主线程执行其他处理");
                task.Wait();
                #endregion
    
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.ReadLine();
            }
    
            static void TaskMethod(string name)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            }
        }
    }
    

    主程序2 async/await的实现方式(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            async static void AsyncFunction()
            {
                await Task.Delay(1);
                Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
                }
            }
    
            public static void Main()
            {
                Console.WriteLine("主线程执行业务处理.");
                AsyncFunction();
                Console.WriteLine("主线程执行其他处理");
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(string.Format("Main:i={0}", i));
                }
                Console.ReadLine();
            }
        }
    }
    

    2-2.带返回值的方式

    方式1代码如下(示例):

    	Task<int> task = CreateTask("Task 1");
      task.Start(); 
      int result = task.Result;
    

    主程序1(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static Task<int> CreateTask(string name)
            {
                return new Task<int>(() => TaskMethod(name));
            }
    
            static void Main(string[] args)
            {
                TaskMethod("Main Thread Task");
                Task<int> task = CreateTask("Task 1");
                task.Start();
                int result = task.Result;
                Console.WriteLine("Task 1 Result is: {0}", result);
    
                task = CreateTask("Task 2");
                //该任务会运行在主线程中
                task.RunSynchronously();
                result = task.Result;
                Console.WriteLine("Task 2 Result is: {0}", result);
    
                task = CreateTask("Task 3");
                Console.WriteLine(task.Status);
                task.Start();
    
                while (!task.IsCompleted)
                {
                    Console.WriteLine(task.Status);
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                }
    
                Console.WriteLine(task.Status);
                result = task.Result;
                Console.WriteLine("Task 3 Result is: {0}", result);
    
                #region 常规使用方式
                //创建任务
                Task<int> getsumtask = new Task<int>(() => Getsum());
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                getsumtask.Start();
                Console.WriteLine("主线程执行其他处理");
                //等待任务的完成执行过程。
                getsumtask.Wait();
                //获得任务的执行结果
                Console.WriteLine("任务执行结果:{0}", getsumtask.Result.ToString());
                #endregion
            }
    
            static int TaskMethod(string name)
            {
                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(2));
                return 42;
            }
    
            static int Getsum()
            {
                int sum = 0;
                Console.WriteLine("使用Task执行异步操作.");
                for (int i = 0; i < 100; i++)
                {
                    sum += i;
                }
                return sum;
            }
        }
    }
    

    主程序2 async/await的实现方式(示例):

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                var ret1 = AsyncGetsum();
                Console.WriteLine("主线程执行其他处理");
                for (int i = 1; i <= 3; i++)
                    Console.WriteLine("Call Main()");
                int result = ret1.Result;                  //阻塞主线程
                Console.WriteLine("任务执行结果:{0}", result);
            }
    
            async static Task<int> AsyncGetsum()
            {
                await Task.Delay(1);
                int sum = 0;
                Console.WriteLine("使用Task执行异步操作.");
                for (int i = 0; i < 100; i++)
                {
                    sum += i;
                }
                return sum;
            }
        }
    }
    

    2-3.组合任务.ContinueWith

    简单Demo(示例):

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                //创建一个任务
                Task<int> task = new Task<int>(() =>
                {
                    int sum = 0;
                    Console.WriteLine("使用Task执行异步操作.");
                    for (int i = 0; i < 100; i++)
                    {
                        sum += i;
                    }
                    return sum;
                });
                //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
                task.Start();
                Console.WriteLine("主线程执行其他处理");
                //任务完成时执行处理。
                Task cwt = task.ContinueWith(t =>
                {
                    Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
                });
                task.Wait();
                cwt.Wait();
            }
        }
    }
    

    任务的串行(示例):

    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                ConcurrentStack<int> stack = new ConcurrentStack<int>();
    
                //t1先串行
                var t1 = Task.Factory.StartNew(() =>
                {
                    stack.Push(1);
                    stack.Push(2);
                });
    
                //t2,t3并行执行
                var t2 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("Task t2 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
                });
    
                //t2,t3并行执行
                var t3 = t1.ContinueWith(t =>
                {
                    int result;
                    stack.TryPop(out result);
                    Console.WriteLine("Task t3 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
                });
    
                //等待t2和t3执行完
                Task.WaitAll(t2, t3);
    
                //t7串行执行
                var t4 = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("当前集合元素个数:{0},Thread id {1}", stack.Count, Thread.CurrentThread.ManagedThreadId);
                });
                t4.Wait();
            }
        }
    }
    

    子任务(示例):

    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            public static void Main()
            {
                Task<string[]> parent = new Task<string[]>(state =>
                {
                    Console.WriteLine(state);
                    string[] result = new string[2];
                    //创建并启动子任务
                    new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
                    new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
                    return result;
                }, "我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
                //任务处理完成后执行的操作
                parent.ContinueWith(t =>
                {
                    Array.ForEach(t.Result, r => Console.WriteLine(r));
                });
                //启动父任务
                parent.Start();
                //等待任务结束 Wait只能等待父线程结束,没办法等到父线程的ContinueWith结束
                //parent.Wait();
                Console.ReadLine();
    
            }
        }
    }
    

    动态并行(TaskCreationOptions.AttachedToParent) 父任务等待所有子任务完成后 整个任务才算完成(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Node
        {
            public Node Left { get; set; }
            public Node Right { get; set; }
            public string Text { get; set; }
        }
    
    
        class Program
        {
            static Node GetNode()
            {
                Node root = new Node
                {
                    Left = new Node
                    {
                        Left = new Node
                        {
                            Text = "L-L"
                        },
                        Right = new Node
                        {
                            Text = "L-R"
                        },
                        Text = "L"
                    },
                    Right = new Node
                    {
                        Left = new Node
                        {
                            Text = "R-L"
                        },
                        Right = new Node
                        {
                            Text = "R-R"
                        },
                        Text = "R"
                    },
                    Text = "Root"
                };
                return root;
            }
    
            static void Main(string[] args)
            {
                Node root = GetNode();
                DisplayTree(root);
            }
    
            static void DisplayTree(Node root)
            {
                var task = Task.Factory.StartNew(() => DisplayNode(root),
                                                CancellationToken.None,
                                                TaskCreationOptions.None,
                                                TaskScheduler.Default);
                task.Wait();
            }
    
            static void DisplayNode(Node current)
            {
    
                if (current.Left != null)
                    Task.Factory.StartNew(() => DisplayNode(current.Left),
                                                CancellationToken.None,
                                                TaskCreationOptions.AttachedToParent,
                                                TaskScheduler.Default);
                if (current.Right != null)
                    Task.Factory.StartNew(() => DisplayNode(current.Right),
                                                CancellationToken.None,
                                                TaskCreationOptions.AttachedToParent,
                                                TaskScheduler.Default);
                Console.WriteLine("当前节点的值为{0};处理的ThreadId={1}", current.Text, Thread.CurrentThread.ManagedThreadId);
            }
        }
    }
    

    取消任务 CancellationTokenSource:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private static int TaskMethod(string name, int seconds, CancellationToken token)
            {
                Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                    name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
                for (int i = 0; i < seconds; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    if (token.IsCancellationRequested) return -1;
                }
                return 42 * seconds;
            }
    
            private static void Main(string[] args)
            {
                var cts = new CancellationTokenSource();
                var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
                Console.WriteLine(longTask.Status);
                cts.Cancel();
                Console.WriteLine(longTask.Status);
                Console.WriteLine("First task has been cancelled before execution");
                cts = new CancellationTokenSource();
                longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
                longTask.Start();
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine(longTask.Status);
                }
                cts.Cancel();
                for (int i = 0; i < 5; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine(longTask.Status);
                }
    
                Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
            }
        }
    }
    

    处理任务中的异常(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            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));
                throw new Exception("Boom!");
                return 42 * seconds;
            }
    
            static void Main(string[] args)
            {
                try
                {
                    Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
                    int result = task.GetAwaiter().GetResult();
                    Console.WriteLine("Result: {0}", result);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
                }
                Console.WriteLine("----------------------------------------------");
                Console.WriteLine();
            }
        }
    }
    

    多个任务(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            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));
                throw new Exception(string.Format("Task {0} Boom!", name));
                return 42 * seconds;
            }
    
    
            public static void Main(string[] args)
            {
                try
                {
                    var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
                    var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
                    var complexTask = Task.WhenAll(t1, t2);
                    var exceptionHandler = complexTask.ContinueWith(t =>
                            Console.WriteLine("Result: {0}", t.Result),
                            TaskContinuationOptions.OnlyOnFaulted
                        );
                    t1.Start();
                    t2.Start();
                    Task.WaitAll(t1, t2);
                }
                catch (AggregateException ex)
                {
                    ex.Handle(exception =>
                    {
                        Console.WriteLine(exception.Message);
                        return true;
                    });
                }
            }
        }
    }
    

    async/await的方式(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static async Task ThrowNotImplementedExceptionAsync()
            {
                throw new NotImplementedException();
            }
    
            static async Task ThrowInvalidOperationExceptionAsync()
            {
                throw new InvalidOperationException();
            }
    
            static async Task Normal()
            {
                await Fun();
            }
    
            static Task Fun()
            {
                return Task.Run(() =>
                {
                    for (int i = 1; i <= 10; i++)
                    {
                        Console.WriteLine("i={0}", i);
                        Thread.Sleep(200);
                    }
                });
            }
    
            static async Task ObserveOneExceptionAsync()
            {
                var task1 = ThrowNotImplementedExceptionAsync();
                var task2 = ThrowInvalidOperationExceptionAsync();
                var task3 = Normal();
    
    
                try
                {
                    //异步的方式
                    Task allTasks = Task.WhenAll(task1, task2, task3);
                    await allTasks;
                    //同步的方式
                    //Task.WaitAll(task1, task2, task3);
                }
                catch (NotImplementedException ex)
                {
                    Console.WriteLine("task1 任务报错!");
                }
                catch (InvalidOperationException ex)
                {
                    Console.WriteLine("task2 任务报错!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("任务报错!");
                }
    
            }
    
            public static void Main()
            {
                Task task = ObserveOneExceptionAsync();
                Console.WriteLine("主线程继续运行........");
                task.Wait();
            }
        }
    }
    

    Task.FromResult的应用(示例):

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static IDictionary<string, string> cache = new Dictionary<string, string>()
            {
                {"0001","A"},
                {"0002","B"},
                {"0003","C"},
                {"0004","D"},
                {"0005","E"},
                {"0006","F"},
            };
    
            public static void Main()
            {
                Task<string> task = GetValueFromCache("0006");
                Console.WriteLine("主程序继续执行。。。。");
                string result = task.Result;
                Console.WriteLine("result={0}", result);
    
            }
    
            private static Task<string> GetValueFromCache(string key)
            {
                Console.WriteLine("GetValueFromCache开始执行。。。。");
                string result = string.Empty;
                //Task.Delay(5000);
                Thread.Sleep(5000);
                Console.WriteLine("GetValueFromCache继续执行。。。。");
                if (cache.TryGetValue(key, out result))
                {
                    return Task.FromResult(result);
                }
                return Task.FromResult("");
            }
    
        }
    }
    

    使用IProgress实现异步编程的进程通知,IProgress只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress的实现类Progress的构造函数接收类型为Action的形参,通过这个委托让进度显示在UI界面中(示例):

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void DoProcessing(IProgress<int> progress)
            {
                for (int i = 0; i <= 100; ++i)
                {
                    Thread.Sleep(100);
                    if (progress != null)
                    {
                        progress.Report(i);
                    }
                }
            }
    
            static async Task Display()
            {
                //当前线程
                var progress = new Progress<int>(percent =>
                {
                    Console.Clear();
                    Console.Write("{0}%", percent);
                });
                //线程池线程
                await Task.Run(() => DoProcessing(progress));
                Console.WriteLine("");
                Console.WriteLine("结束");
            }
    
            public static void Main()
            {
                Task task = Display();
                task.Wait();
            }
        }
    }
    

    Factory.FromAsync的应用 (简APM模式(委托)转换为任务)(BeginXXX和EndXXX)(示例)带回调方式的:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            private delegate string AsynchronousTask(string threadName);
    
            private static string Test(string threadName)
            {
                Console.WriteLine("Starting...");
                Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Thread.CurrentThread.Name = threadName;
                return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
            }
    
            private static void Callback(IAsyncResult ar)
            {
                Console.WriteLine("Starting a callback...");
                Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
                Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
                Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
            }
    
            //执行的流程是 先执行Test--->Callback--->task.ContinueWith
            static void Main(string[] args)
            {
                AsynchronousTask d = Test;
                Console.WriteLine("Option 1");
                Task<string> task = Task<string>.Factory.FromAsync(
                    d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
    
                task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
                    t.Result));
    
                while (!task.IsCompleted)
                {
                    Console.WriteLine(task.Status);
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                }
                Console.WriteLine(task.Status);
    
            }
        }
    }
    

    Factory.FromAsync的应用 (简APM模式(委托)转换为任务)(BeginXXX和EndXXX)(示例)不带回调方式的:

    //Task启动带参数和返回值的函数任务
    //下面的例子test2 是个带参数和返回值的函数。
    
    private int test2(object i)
    {
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = true;
        }));
        System.Threading.Thread.Sleep(3000);
        MessageBox.Show("hello:" + i);
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = false;
        }));
        return 0;
    }
    
    //测试调用
    private void call()
    {
        //Func<string, string> funcOne = delegate(string s){ return "fff"; };
        object i = 55;
        var t = Task<int>.Factory.StartNew(new Func<object, int>(test2), i);
    }
    
    //= 下载网站源文件例子 == == == == == == == == == == == ==
    //HttpClient 引用System.Net.Http
    private async Task< int> test2(object i)
    {
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = true;
        }));
    
        HttpClient client = new HttpClient();
        var a = await client.GetAsync("http://www.baidu.com");
        Task<string> s = a.Content.ReadAsStringAsync();
        MessageBox.Show (s.Result);
    
        //System.Threading.Thread.Sleep(3000);
        //MessageBox.Show("hello:"+ i);
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = false;
        }));
        return 0;
    }
    
    async private void call()
    {
        //Func<string, string> funcOne = delegate(string s){ return "fff"; };
        object i = 55;
        var t = Task<Task<int>>.Factory.StartNew(new Func<object, Task<int>>(test2), i);
    }
    
    //----------或者----------
    
    private async void test2()
    {
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = true;
        }));
        HttpClient client = new HttpClient();
        var a = await client.GetAsync("http://www.baidu.com");
        Task<string> s = a.Content.ReadAsStringAsync();
        MessageBox.Show (s.Result);
        this.Invoke(new Action(() =>
        {
            pictureBox1.Visible = false;
        }));
    }
    
    private void call()
    {
        var t = Task.Run(new Action(test2));
        //相当于
        //Thread th= new Thread(new ThreadStart(test2));
        //th.Start();
    }
    

    TTask启动带参数和返回值的函数任务,下面的例子test2 是个带参数和返回值的函数(示例):


    总结

    提示:这里对文章进行总结:
    例如:以上就是今天要讲的内容,本文仅仅简单介绍了Task的使用,而Task提供了大量能使我们快速便捷地处理数据的函数和方法。

    更多相关内容
  • C# Task

    千次阅读 多人点赞 2020-02-18 19:30:57
    Task作为C#异步的核心,类中的每个方法有必要学习一番,而部分重点方法更要尝试分析一下源码。 首先,Task位于System.Threading.Tasks命名空间下。 官方对其定义:Represents an asynchronous operation. 先看...

    Task作为C#异步的核心,类中的每个方法有必要学习一番,而部分重点方法更要尝试分析一下源码。

    首先,Task位于System.Threading.Tasks命名空间下。

    官方对其定义:Represents an asynchronous operation.

    先看一下Task的类注释,这里讲了很多重点。

    第一条注释:

    Task instances may be created in a variety of ways.The most common approach is by using the Task type's Factory property to retrieve a System.Threading.Tasks.TaskFactory instance that can be used to create tasks for serveral purposes

    也就是说,我们可以使用Task.Factory.StartNew这个方法的很多重载方式去创建一个适合应用场景的Task,下面是应用实例:

    static void Main(string[] args)
    {
         Console.WriteLine("Main1 Thread" + Thread.CurrentThread.ManagedThreadId);
         Task.Factory.StartNew(() => 
         {
             Console.WriteLine("Main2 Thread" + Thread.CurrentThread.ManagedThreadId);
         });
         Console.ReadLine();
    }
    
    //结果:
    Main1 Thread1
    Main2 Thread3

    第二条注释:

    The Task class also provides constructors that initialize the Task but that do not schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation and scheduling must be separated, the constructors may be used, and the task's Start() method may then be used to schedule the task for execution at a later time.

    意思就是

    如果你想建立一个Task并且立即执行它,使用Task.Factory.StartNew(),这样做性能更好

    如果你想建立一个Task,想在合适的时候执行它,那么使用Task构造器做初始化:

    static void Main(string[] args)
    {
        //初始化这个task,但不执行
        Task task = new Task(() => 
        {
           //doSomething
        });
        //做第一件事
        //做第二件事
        //做第N件事
        //之后再执行这个task
        task.Start();
        Task.Factory.StartNew(() => { });
    }

    这样更加灵活。

    第三条注释:

    All members of Task, except for Dispose(),are thread-safe and may be used from multiple threads concurrently.

    除了Dispose()方法,Task的其他所有成员都是线程安全的!

    第四条注释:

    For operations that return values, the System.Threading.Tasks.Task{TResult} class should be used.

    如果你想使用有返回值的Task,可以使用Task<Tresult>:

    Task<string> task = new Task<string>(() =>
    {
        //doSomething
        return "可以返回泛型类型的值";
    });
    Console.WriteLine("task的返回值是:" + task.Result);
    
    输出:
    可以返回泛型类型的值

    最后一条注释,表述了一些成员变量的作用:

    For developers implementing custom debuggers, several internal and private members of Task may be useful (these may change from release to release).
    ①The Int32 m_taskId field serves as the backing store for the Id property, however accessing this field directly from a debugger may be more efficient than accessing the same value through the property's getter method (the s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). 
    ②the Int32 m_stateFlags field stores information about the current lifecycle stage of the Task,information also accessible through the Status property. 
    ③The m_action System.Object field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the InternalWait method serves a potential marker for when a Task is entering a wait operation.

    ①直接使用m_taskId比使用ID的getter方法更好,因为其实可以从源码看到:

    public int Id 
    { 
        get 
        { 
            if (m_taskId == 0) 
            { 
                int newId = NewId(); 
                Interlocked.CompareExchange(ref m_taskId, newId, 0); 
            } 
            return m_taskId; 
        } 
    }

    Id的值是从m_taskId中取的。

    ②m_stateFlags和Status都记录了当前Task的生命周期阶段,而Status的值是从m_stateFlags中取的:

    public TaskStatus Status
    {
        get
        {
            TaskStatus rval;
    
            int sf = m_stateFlags;
     
            if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
            else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
            else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
            else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
            else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
            else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
            else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
            else rval = TaskStatus.Created;
     
            return rval;
         }
     }

    ③m_action存储Task的委托的引用,m_stateObject存储异步状态。然而当我想用vs开始一些测试时,发现根本调用不了这些成员,这里我就想到了我之前从《CLR via C#》当中看到的这么一段话:

    高级语言(包括C#)通常只公开了CLR全部功能的一个子集。然而IL汇编语言允许开发人员访问CLR的全部功能。

    所以,如果我想探寻这些变量的秘密,或许我嘚从IL入手(这是后面再回顾笔记的留言 :这里根本不是因为上面这些鬼扯,明明就是这两个变量是internal的访问权限而已...)。

    ④InternalWait()方法会在Wait()方法中被调用,作为判断task是否完成的一个标记。

    public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
    {
    	//......
        // Wait, and then return if we're still not done.
        if (!InternalWait(millisecondsTimeout, cancellationToken))
            return false;
        //......
    }

    说完了Task的重点注释之后,我们来看一下Task实现了哪些接口:

    IAsyncResult:

    encapsulate the results of an async operation 封装异步的操作结果

    成员:

    //判断异步操作是否完成
    bool IsCompleted 
    //该对象主要是有一些等待异步操作完成的动作
    WaitHandle AsyncWaitHandle
    //可设置的异步状态,比较灵活
    Object AsyncState
    //判断异步操作是否同步完成
    bool CompeltedSynchronously

    IDisposable:

    Interface for assisting with deterministic finalization.用于释放资源的接口.

    成员:

    //做资源释放的动作
    void Dispose()

    好了,基类看完了,再来看一下Task类的重点,属性与方法。

    首先,在学习时,应注重两点:

    ①作用是什么

    ②如何使用(实践操作)

    那么我们需要先知道去初始化Task对象,这里Task提供了一共8个构造器:

    public Task(Action action);
    public Task(Action action, CancellationToken cancellationToken);
    public Task(Action action, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state);
    public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state, CancellationToken cancellationToken);
    public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
    public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);

    不同的构造器实用于不同的场景,先来看第一个:

    public Task(Action action);

    ①以一个Action委托作为入参的Task构造器:

    static void Main(string[] args)
    {
        Action action = () => 
        {
            Console.WriteLine("The delegate method is executed");
        };
        Task task = new Task(action);
        task.Start();
        Console.ReadLine();
    }

    out:

    第一个比较简单,也比较常用,通常我们会将任务放到Action中然后给Task执行。

    ②Action<obejct>,object作为入参的Task构造器:

    public Task(Action<object> action, object state);
    static void Main(string[] args)
    {
        Action<object> action = (o) => 
        {
            Console.WriteLine("The delegate method is executed,state is :" + o);
        };
        object obj = "success";
        Task task = new Task(action,obj);
        task.Start();
        Console.ReadLine();
    }

    out:

    obj会被作为action的入参。

    ③Action,CancellationToken作为入参的Task构造器:

    public Task(Action action, CancellationToken cancellationToken); 
    static void Main(string[] args)
    {
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
    
        Action action = () =>
        {
    
            Console.WriteLine("I have been performed");
            ct.ThrowIfCancellationRequested();
            Console.WriteLine("Maybe I won't be executed");
        };
        Task task = new Task(action);
        task.Start();
        tokenSource.Cancel();
        Console.ReadLine();
    }

    out:

    主程序中,执行了Cancel方法,所以在Task执行任务时调用ThrowIfCancellationRequested方法检测到了Cancel操作,遂中止task,抛出异常,但因异常在Task线程,所以不会对主线程造成影响。

    这种方式创建的Task可以起到相应控制的作用。

    ④以Action,TaskCreationOptions作为Task入参的构造器:

    TaskCreationOptions是一个枚举类,一共有7个枚举值:

    参考:https://www.licc.tech/article?id=53

    public enum TaskCreationOptions
    {
        None = 0,
        PreferFairness = 1,
        LongRunning = 2,
        AttachedToParent = 4,
        DenyChildAttach = 8,
        HideScheduler = 16,
        RunContinuationsAsynchronously = 64
    }	

    TaskCreationOptions.None:一个一个来试一下吧:

    static void Main(string[] args)
    {
       Action action = () =>
       {
           Console.WriteLine("The delegate is executing");
       };
       TaskCreationOptions creationOptions = TaskCreationOptions.None;
       Task task = new Task(action, creationOptions);
       task.Start();
       Console.ReadLine();
    }

    TaskCreationOptions.PreferFairness:枚举值的描述是指定默认行为,所以可能这个枚举值设定和不设定应该对Task没有影响吧。

    提示TaskScheduler以一种公平的方式去执行Task,越早计划的Task会越早执行,相反,越晚计划的Task将会越晚执行。用例待补充。

    TaskCreationOptions.LongRunning:

    默认的TaskScheduler采用的是.NET线程池ThreadPool,它主要面向的是细粒度的小任务,其执行时间通常在毫秒级。线程池中的线程数与处理器的内核数有关,如果线程池中没有空闲的线程,那么后续的Task将会被阻塞。因此,如果事先知道一个Task的执行需要较长的时间,就需要使用TaskCreationOptions.LongRunning枚举指明。使用TaskCreationOptions.LongRunning创建的任务将会脱离线程池启动一个单独的线程来执行。

    先来看看本机有的cpu是几核的:

    现在设定一个任务,里面进行while(true){}循环让线程永远被占用,4核就跑5个task:

    static void Main(string[] args)
    {
        Action action = () =>
        {
            Console.WriteLine("The delegate is executing:" + Thread.CurrentThread.ManagedThreadId);
            while (true){ }
        };
        TaskCreationOptions creationOptions = TaskCreationOptions.None;
        //启动4个task
        for(int i = 0; i < 4; i++)
        {
    		Task.Run(action);
        }
    	//启动第5个task
        Task task = new Task(action, creationOptions);
        task.Start();
        Console.ReadLine();
    }

    结果:

    可以看到只有4个task被启动,第5个task被阻塞了。

    当使用第5个Task使用TaskCreationOptions.LongRunning之后

    static void Main(string[] args)
    {
        Action action = () =>
        {
            Console.WriteLine("The delegate is executing:" + Thread.CurrentThread.ManagedThreadId);
            while (true){ }
        };
        TaskCreationOptions creationOptions = TaskCreationOptions.LongRunning;
        //启动4个task
        for(int i = 0; i < 4; i++)
        {
    		Task.Run(action);
        }
    	//启动第5个task
        Task task = new Task(action, creationOptions);
        task.Start();
        Console.ReadLine();
    }

    结果:

    第5个task正常运行。

    TaskCreationOptions.AttachedToParent:

    正常情况下,如果在一个Task内部再建一个Task,两个Task位于2个线程,互相不会影响

    static void Main(string[] args)
    {
        TaskCreationOptions creationOptions = TaskCreationOptions.None;
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, creationOptions);
        });
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    可以看到,外层Task执行完毕,而后内层Task才执行完毕。

    现在来让外层Task等一下内层Task:

    static void Main(string[] args)
    {
        TaskCreationOptions creationOptions = TaskCreationOptions.AttachedToParent;
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, creationOptions);
        });
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    可以看到,外层Task会被wait住,等待内层Task执行完成,然后整个Task才会执行完成。

    另外,通过对内层Task设定TaskCreationOptions.AttachedToParent,内层Task抛出异常外层Task也可捕获处理。

    TaskCreationOptions.DenyChildAttach:

    如果内层设定了TaskCreationOptions.AttachedToParent让外层去等待自己执行完成,但外层不想等待,就可以给外层设定TaskCreationOptions.DenyChildAttach:

    static void Main(string[] args)
    {
        var parent = Task.Factory.StartNew(() =>
        {
             Console.WriteLine("parent is executing");
             var child = Task.Factory.StartNew(() =>
             {
                Console.WriteLine("child is executing,Father wating me");
                Thread.Sleep(10000);
                Console.WriteLine("child task completing");
              }, TaskCreationOptions.AttachedToParent);
        },TaskCreationOptions.DenyChildAttach);
        parent.Wait();
        Console.WriteLine("Outer has completed");
        Console.ReadLine();
    }

    结果:

    这样外层Task又会忽略内层Task的执行了。

    TaskCreationOptions.HideScheduler:

    //创建这个Task使用TaskScheduler.Current
    Task task = Task.Factory.StartNew(action);
    //创建这个Task使用TaskScheduler.Default
    Task task1 = Task.Factory.StartNew(action, TaskCreationOptions.HideScheduler);

    但如何证实具体使用的哪个TaskScheduler以及两个TaskScheduler的区别还不清楚。

    TaskCreationOptions.RunContinuationsAsynchronously 待补充

    剩余的其他构造方法便是Action,Action<object>,CancellationToken,TaskCreationOptions几个参数的不同组合了,到底运用哪个Task就要看实际的场景。

    现在再来看一下Task的所有属性:

    属性作用
    static int CurrentId 返回当前正在执行的Task的ID
    static TaskFactory Factory 创建Task的工厂类
    static Task CompletedTask 获取已成功完成的Task。
    TaskCreationOptions CreationOptions获取创建Task时指定的TaskCreationOptions 
    bool IsCompleted判断当前Task是否完成执行
    bool IsCanceled 判断当前Task是否由于被取消而完成
    TaskStatus Status获取当前Task的状态
    AggregateException Exception获取当前Task是否是因为过早结束而抛出的异常,如果当前Task成功的执行完成,那么会返回Null.
    int Id获取当前Task实例的ID
    object AsyncState获取当前Task的异步状态
    bool IsFaulted判断当前Task是否由于未处理异常而完成

    CurrentId:

    static void Main(string[] args)
    { 
        Task task = new Task(() =>
        {
             Console.WriteLine("CurrentId is " + Task.CurrentId);
         });
             Task task1 = new Task(() =>
             {
             Console.WriteLine("CurrentId is " + Task.CurrentId);
         });
         task.Start();
         task1.Start();
         Console.ReadLine();
    }

    结果:

    Id和CurrenctId看起来很像,并且也是同一个值,只是CurrentId只能在task的运行环境中获取。而Id就比较灵活了,因为是实例相关,只要可以使用该task实例对象的地方都可以获取到实例task的Id.

    static void Main(string[] args)
    { 
        Task task = new Task(() =>
        {
            Console.WriteLine("CurrentId is " + Task.CurrentId);
        });
        task.Start();
        Console.WriteLine("Id is " + task.Id);
        Console.ReadLine();
    }

    结果:

    Factory:

    Factory顾名思义,创建Task的工厂类:

    public class Program
    {
        static void Main(string[] args)
        {
            //1.最常见的方式
            Task<string> task = Task.Factory.StartNew(() => { return "I am the return value of task"; });
            //2.创建的task的委托方法运行条件是等到传入的异步操作完成
            Task.Factory.FromAsync(task, (asyncResult) => 
            {
                Task<string> task1 = (Task<string>)asyncResult;
                Console.WriteLine("第二种方式:" + task1.Result + "---------------------");
            });
            //3.当传入的一组task都完成之后执行委托方法
            Task<string> task2 = Task.Factory.StartNew(() => { return "I am the return value of task2"; });
            Task<string> task3 = Task.Factory.StartNew(() => { return "I am the return value of task3"; });
            Task<string>[] tasks = { task2, task3 };
            Task.Factory.ContinueWhenAll(tasks, (o) =>
             {
                 for (int i = 0; i < o.Length; i++)
                 {
                     Console.WriteLine("第三种方式:" + o[i].Result);
                 }
                  Console.WriteLine("-------------------------");
             });
            //4.传入的一组task中的任何一个完成,完成的那个task将继续执行委托方法
            Task.Factory.ContinueWhenAny(tasks, (o) =>
             {
                 Console.WriteLine("第四种方式:" + o.Result);
                 Console.WriteLine("-------------------------");
             });
            Console.ReadLine();
        }
    }

    结果:

     Task.CompletedTask:

    这个属性看起来像是拿到我们之前执行完成的Task,对吧?所以我的想法是构造许多Task,然后之后再调这个方法,得到之前构造中的Task中的一个:

    static void Main(string[] args)
    {
        Task.Run(() => { Console.WriteLine("task1:" + Task.CurrentId); });
        Task.Run(() => { Console.WriteLine("task2:" + Task.CurrentId); });
        //等待上面Task完成
        Thread.Sleep(100);
        Console.WriteLine("Task.CompletedTask id:" + Task.CompletedTask.Id);
        Console.ReadLine();
    }

    但是执行结果总是不尽人意:

    无论如何执行多少次,或者增加多少个task,总是得不到之前执行完成的Task。为什么呢?

    来看看源码吧:

    可以看到,如果是第一次调用这个属性,那么它会新建一个已经完成的Task,之后再调用也会返回同样的Task实例,和你执行的其他task没有任何关系,那么拿到这个Task有什么用呢?

    参考:https://stackoverflow.com/questions/44096253/await-task-completedtask-for-what/44096289

    这篇文章解释的很好,就是如果你想构造一个异步方法,但是现在又没有可以执行的异步逻辑,而又为了以后不为了新增异步逻辑而将同步方法重构为异步方法,那么就使用await Task.CompletedTask这个属性,当做一个占位符,说明这里将来会有异步逻辑的加入。所以这个属性是为了提升程序可扩展性而使用的。

    Task.CreationOptions:

    上面初始化Task时已经说过。

    Task.IsCompleted:

    static void Main(string[] args)
    {
       Task task = Task.Run(() => 
       {
           Thread.Sleep(50);
       });
       int count = 0;
       while (true)
       {
            if (count == 5 && task.IsCompleted)
            {
                Console.WriteLine("task已执行完成");
                break;
            }
            else
            {
                Console.WriteLine("在等待task完成之前先做其他事情");
                Thread.Sleep(10);
                if (count == 5)
                {
                    count = 0;
                }
                count++;
            }
        }
        Console.ReadLine();
    }

    结果:

    这个方法用于判断当前task是否已经执行完成,相较于其他的如Wait这类必须在等待task完成前阻塞线程的方法比起来,该属性的好处是线程可以留出时间给到其他程序,不用完全关注这个task是否完成,而是在需要的时候再去看它是否完成。

    Task.IsCanceled:

    我们先来看一下task会因异常中断而把IsCanceled属性置为true吗:

    static void Main(string[] args)
    {
        Task task = new Task(() =>
        {
            throw new Exception();
        });
        Thread.Sleep(100);
        Console.WriteLine("task因异常中断,task.IsCanceled值是:" + task.IsCanceled);
        Console.ReadLine();
    }

    结果:

    不会,那么我们从源码中对这个属性的标记描述可以知道它会在什么时候被设置为true:

    那就来试一下吧:

    ①在task开始执行之前标记cancellation:

    static void Main(string[] args)
    {
    
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
        Task task = new Task(() => { }, ct);
        //marked for cacellation before in started executing
        tokenSource.Cancel();
        task.Start();
        Console.WriteLine("task.IsCanceled:" + task.IsCanceled);
        Console.ReadLine();
    }

    不出所料:

    ②在程序中抛出OperationCanceledException或调用CancellationToken.ThrowIfCancellationRequested():

    static void Main(string[] args)
    {
        var tokenSource = new CancellationTokenSource();
        CancellationToken ct = tokenSource.Token;
        Task task = new Task(() => 
        {
            throw new OperationCanceledException();
            //or ct.ThrowIfCancellationRequested();
        }, ct);
        task.Start();
        tokenSource.Cancel();
        Thread.Sleep(10);
        Console.WriteLine("task.IsCanceled:" + task.IsCanceled);
        Console.ReadLine();
    }

    结果:

    Task.IsFaulted:

    随着task抛出未处理异常,在IsFaulted为真的情况下,Status会被设置为Faulted,Exception会记录异常。

    static void Main(string[] args)
    {
        Task task = new Task(() =>
        {
            throw new FileNotFoundException();
        });
        task.Start();
        task.ContinueWith((t) =>
        {
            Console.WriteLine("task.IsFaulted: " + t.IsFaulted);
            Console.WriteLine("task.Exception is null ? " + t.Exception == null);
            Console.WriteLine("task.Status:" + t.Status);
        });
        Console.ReadLine();
    }

    结果:

    Task.Exception:

    这个属性用于记录task抛出的异常,用IsFaulted那个例子即可:

    Task.AsyncState:

    最开始尝试直接执行一个task然后调用这个属性为null无果后,通过查看源码,得知可以从Task.Factory.FromAsync去设置这个值:

    这一步可以看到,AsyncState的值依赖于一个私有属性m_stateObject:

    这个私有属性又依赖state,通过一步一步最后找到了调用方:

    所以,就可以尝试用一下这个方法为这个属性赋值了:

    static void Main(string[] args)
    {
        Task task = Task.Factory.StartNew(() => { });
        Func<AsyncCallback, object, IAsyncResult> beginMethod = (asyncCallback, obj) => 
        {
            Console.WriteLine("beginMethod obj: " + obj);
            return task;
        };
        Action<IAsyncResult> endMethod = (asyncResult) => {};
        object state = "test asyncState"; 
        Task task1 = Task.Factory.FromAsync(beginMethod, endMethod, state);
        Console.WriteLine("task1 asyncState:" + task1.AsyncState);
        Console.ReadLine();
    }

    不出所料:

    好了,Task的属性说完了,现在来看一下它所包含的方法,几乎每个方法都有重载,其实只用看它重载方法中的一个就好了,其他就看场景选择:

    void Start()

    Starts the Task, scheduling it for execution to the current TaskScheduler.使用当前TaskScheduler去执行当前Task.

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("Current taskScheduler:" + TaskScheduler.Current.Id);
        });
        task.Start();
        Console.ReadLine();
    }

    结果:

    如果你不想使用默认的Scheduler去执行这个task,还可以使用带TaskScheduler参数的Start去自定义Scheduler。

    void Wait() //Waits for the Task to complete execution.等待task执行完成。

    static void Main(string[] args)
    {
        Task task = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        task.Wait();
        Console.WriteLine("当前task状态:" + task.Status);
        Console.ReadLine();
    }

    结果:

    使用这个方法,会阻塞式的等待task完成,如果想稍微灵活点,可以考虑使用带timeout参数的Wait方法。

    void RunSynchronously()//Runs the Task synchronously on the current TaskScheduler.使用scheduler同步运行当前Task.

    注意:

    使用该方法应保证task从未被执行过。

    根据MSDN介绍:

    Ordinarily, tasks are executed asynchronously on a thread pool thread and do not block the calling thread. Tasks executed by calling the RunSynchronously() method are associated with the current TaskScheduler and are run on the calling thread. If the target scheduler does not support running this task on the calling thread, the task will be scheduled for execution on the schedule, and the calling thread will block until the task has completed execution

    也就是说,使用这个方法会有两种方式同步执行task:

    ①直接使用当前线程执行

    ②另起一个线程执行,但会阻塞当前线程,相当于Start+Wait.

    看看例子:

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("使用RunSynchronously task会同步执行");
            Thread.Sleep(1000);
        });
        task.RunSynchronously();
        Console.WriteLine("当前task状态:" + task.Status);
        Console.ReadLine();
    }

    结果:

    Task ContinueWith(Action<Task> continuationAction) //Creates a continuation that executes asynchronously when the target Task completes. 当task执行完成继续执行continuationAction。

    static void Main(string[] args)
    {
        Task task = new Task(() => 
        {
            Console.WriteLine("正在学习ContinueWith方法 " + 
                              Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(1000);
        });
        task.Start();
        Console.WriteLine("taskId:" + task.Id + ",task1 instance:" + task.GetHashCode());
        Task task1 = task.ContinueWith((t) =>
        {
            Console.WriteLine("我正在使用task继续执行任务 " + 
                              Thread.CurrentThread.ManagedThreadId);
        });
        Console.WriteLine("task1Id:" + task1.Id + ",task1 instance:" + task.GetHashCode());
        Console.ReadLine();
    }

    结果:

    通过结果可以知道:

    ①继续执行的任务和原task不被同一个线程执行

    ②虽是同一个task在继续执行任务,但ID不同了

    ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)

    https://blog.csdn.net/qq_38312617/article/details/104446307,这个文章有说到。

    YieldAwaitable Yield()

    对于官方解释,没有看太懂,大概意思好像是使用Yield,如果SynchronizationContext非null,那么await Task.Yield()之后代码会被列为优先级低的代码执行(依然会同步执行,只是暂时执行其他代码).如果是null,那么await Task.Yield()会被异步执行。

    这篇文章有相应解释,大概也印证了我看到的逻辑:

    https://stackoverflow.com/questions/22645024/when-would-i-use-task-yield

    从代码看一下吧:

    static void Main(string[] args)
    {
        TestYield();
        Console.WriteLine("main thread:" + Thread.CurrentThread.ManagedThreadId);
        Console.ReadLine();
    }
    public async static void TestYield()
    {
        //Dosomething important
        Thread.Sleep(500);
        Console.WriteLine("TestYiled1 thread is " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine(SynchronizationContext.Current == null);
        await Task.Yield();
        //Unimportant
        Thread.Sleep(500);
        Console.WriteLine("TestYiled2 thread is " + Thread.CurrentThread.ManagedThreadId);
    }

    结果:

    果然,在SynchronizationContext为null的情况,后续代码在新的上下文中被执行。

    Task WhenAll(params Task[] tasks) //Creates a task that will complete when all of the Task objects in an array have completed. 建立一个task,等待所有task完成时该task完成

    如果你觉得一个一个处理等待task太麻烦,那么就可以使用这个方法去收集所有你要等待的task统一处理就好,就比如说,我有3个计算题要计算,我不需要你做完一个回复一次,这样没有必要,我只需要所有计算题做完就好:

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            int calc = 1 + 1; 
        });
        Task task2 = Task.Run(() => 
        {
            int calc = 2 + 2;
        });
        Task task3 = Task.Run(() =>
        {
            int calc = 3 + 3;
        });
        Task task4 = Task.WhenAll(task1, task2,task3);
        task4.Wait();
        if (task4.IsCompleted)
        {
            Console.WriteLine("task1、task2、task3 is completed calc");
        }
            Console.ReadLine();
        }

    结果:

    Task<Task> WhenAny(params Task[] tasks)//Creates a task that will complete when any of the supplied tasks have completed. 建立一个task,等待任务中的任何一个任务完成时完成。

    举个例子,你开启了3个抢票任务,有个抢票成功就可以通知你了,就可以利用这个方法:

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            Thread.Sleep(100);
            Console.WriteLine(" buy a ticket");
        });
        Task task2 = Task.Run(() => 
        {
            Thread.Sleep(200);
            Console.WriteLine(" buy a ticket");
        });
        Task task3 = Task.Run(() =>
        {
            Thread.Sleep(300);
            Console.WriteLine(" buy a ticket");
        });
        Task task4 = Task.WhenAny(task1, task2,task3);
        task4.Wait();
        if (task4.IsCompleted)
        {
             Console.WriteLine("Buy a ticket to complete~");
        }
             Console.ReadLine();
        }
    }

    结果:

    bool WaitAll(Task[] tasks, TimeSpan timeout)//Waits for all of the provided cancellable Task objects to complete execution within a specified time interval.在指定时间等待所有的task完成。

    static void Main(string[] args)
    {
        Task task1 = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        Task task2 = Task.Run(() => 
        {
            Thread.Sleep(1000);
        });
        Task task3 = Task.Run(() =>
        {
             Thread.Sleep(1000);
        });
        Task[] tasks = { task1, task2, task3 };
        bool isCompleted = Task.WaitAll(tasks, TimeSpan.FromMilliseconds(2000));
        if (isCompleted)
        {
             Console.WriteLine("All of task is exeute completed");
        }
        Console.ReadLine();
     }

    结果:

    Task<TResult> Run<TResult>(Func<Task<TResult>> function)//Queues the specified work to run on the ThreadPool and returns a proxy for the Task(TResult) returned by function. 将任务放到线程池中排队等待运行,并且返回一个Task<TResult>的代理task.

    总之,Run方法就是拿来创建并执行任务的。

    static void Main(string[] args)
    {
        Func<Task<string>> func = () =>
        {
             Task<string> task = Task.Run(() => { return "0.0"; });
             return task;
        };
        Task<string> task1 = Task.Run(func);
        Console.WriteLine(task1.Result);
        Console.ReadLine(); 
    }

    结果:

    Task<TResult> FromResult<TResult>(TResult result)//Creates a Task`1 that's completed successfully with the specified result.//就是把TResult的值作为返回值task.Result的值。

    通过这篇文章可以知道这个方法的一些作用:

    https://stackoverflow.com/questions/19568280/what-is-the-use-for-task-fromresulttresult-in-c-sharp

    There are two common use cases I've found:

    1. When you're implementing an interface that allows asynchronous callers, but your implementation is synchronous.//可使用await Task.FromResult;设计一个异步方法,但方法内部是同步的
    2. When you're stubbing/mocking asynchronous code for testing.//做异步测试
    public async static void TestYield()
    {
        await Task<string>.FromResult("0.0");
    }

    Task<TResult> FromException<TResult>(Exception exception)//Creates a Task`1 that's completed with a specified exception.建立一个有指定异常的task.

    看文章:

    https://stackoverflow.com/questions/56444538/correct-usage-of-return-task-fromexception

    static void Main(string[] args)
    {
        Task task = TestYield();
        Console.WriteLine(task.Exception);
        Console.ReadLine();
    }
    public  static Task TestYield()
    {
        return Task.FromException<string>(new Exception("Something err"));
    }

    结果:

    Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)\\ Creates a Task that's completed due to cancellation with a specified cancellation token. 创建一个由于指定cancellation token取消而完成的task。

    与FromException类似。

    Task Delay(int millisecondsDelay)//Creates a task that completes after a time delay.创建延迟指定时间完成的task.

    static void Main(string[] args)
    {
        Task task = Task.Delay(1000);
        task.Wait();
        if (task.IsCompleted)
        {
            Console.WriteLine("task is execute completed");
        }
        Console.ReadLine();
    }

    结果:

    至此,Task中所有的继承、属性、方法都做了一遍了解,知道了基础用法,但还有很多方法不知道具体场景,这可能需要在以后在相应场景使用的了再回过头来补充。虽然思考场景是深入认识这个类的一个很好的方式,但无奈不能花费过多的时间在这一件事上,因为还有太多了不认识正等待着我去认识。

    展开全文
  • C# Task任务队列

    千次阅读 2021-05-28 14:25:31
    目录 一、需求 二、基本的Task用法 三、让Task任务按顺序执行 四、使用异步委托解决UI界面卡死问题 五、异步任务队列按顺序执行 六、封装任务队列 七、封装任务队列优化版 结束 一、需求 众所周知,方法体内代码是从...

    目录

    一、需求

    二、基本的Task用法

    三、让Task任务按顺序执行

    四、使用异步委托解决UI界面卡死问题

    五、异步任务队列按顺序执行

    六、封装任务队列

    七、封装任务队列优化版

    结束


    一、需求

    众所周知,方法体内代码是从上往下执行的,在我们工作中经常会遇到一些需要延时执行,但又必须按顺序来执行的需求。这要怎么解决呢。微软官方提供的Task API就是专门来解决这个问题的。那么下面就开始吧。

    二、基本的Task用法

    新建一个Winfrom项目

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace 线程2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                Task task1 = new Task(() =>
                {
                    Thread.Sleep(400);
                    Console.WriteLine("task1");
                });
                Task task2 = new Task(() =>
                {
                    Thread.Sleep(300);
                    Console.WriteLine("task2");
                });
                Task task3 = new Task(() =>
                {
                    Thread.Sleep(200);
                    Console.WriteLine("task3");
                });
                Task task4 = new Task(() =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine("task4");
                });
                task1.Start();
                task2.Start();
                task3.Start();
                task4.Start();
            }
        }
    }
    

    运行:

    由于各个任务内部延时不同,最先执行的Task1,反而最后一个执行完,如果既要做延时操作,又要求从任务按顺序执行,要怎么解决呢?

    三、让Task任务按顺序执行

    修改代码:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace 线程2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private List<Task> TaskList = new List<Task>();
    
            private void Form1_Load(object sender, EventArgs e)
            {
                Task task1 = new Task(() =>
                {
                    Thread.Sleep(400);
                    Console.WriteLine("task1");
                });
                Task task2 = new Task(() =>
                {
                    Thread.Sleep(300);
                    Console.WriteLine("task2");
                });
                Task task3 = new Task(() =>
                {
                    Thread.Sleep(200);
                    Console.WriteLine("task3");
                });
                Task task4 = new Task(() =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine("task4");
                });
    
                TaskList.Add(task1);
                TaskList.Add(task2);
                TaskList.Add(task3);
                TaskList.Add(task4);
    
                foreach (Task task in TaskList)
                {
                    task.Start();
                    task.Wait();
                }
            }
        }
    }
    

    运行:

    用上面的方法虽然有效,你可以看看,点击界面的时候,界面处鼠标指针会一直转圈,导致winfrom界面卡住,无法操作,这是因为使用Thread.Sleep 导致主线程阻塞,下面就来解决UI界面卡死的问题。

    四、使用异步委托解决UI界面卡死问题

    代码:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace 线程2
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
    
            private List<Task> TaskList = new List<Task>();
    
    
            private void Button_Calculate_Click(object sender, EventArgs e)
            {
                Task task1 = new Task(async () =>
                {
                    await Task.Delay(TimeSpan.FromSeconds(4));
                    Console.WriteLine("task1");
                });
                Task task2 = new Task(async () =>
                {
                    await Task.Delay(TimeSpan.FromSeconds(3));
                    Console.WriteLine("task2");
                });
                Task task3 = new Task(async () =>
                {
                    await Task.Delay(TimeSpan.FromSeconds(2));
                    Console.WriteLine("task3");
                });
                Task task4 = new Task(async () =>
                {
                    await Task.Delay(TimeSpan.FromSeconds(1));
                    Console.WriteLine("task4");
                });
    
                TaskList.Add(task1);
                TaskList.Add(task2);
                TaskList.Add(task3);
                TaskList.Add(task4);
    
                foreach (Task task in TaskList)
                {
                    task.Start();
                    task.Wait();
                }
            }
        }
    }
    

    运行:

    用异步方式虽然界面不会卡住,但另一个问题来了,task.wait()方法似乎没有效果。里面的任务队列依然没有按顺序来执行。那么如何即使用异步执行,也不阻塞主线程,而且要任务按顺序来执行呢?

    五、异步任务队列按顺序执行

    代码:

    private void Test()
    {
        Task.Run(() =>
        {
            Task t1 = new Task(() => {
                Thread.Sleep(2000);
                Console.WriteLine("t1");
                num = 1;
            });
            t1.Start();
            t1.Wait();
            Task t2 = new Task(() => {
                Thread.Sleep(1000);
                Console.WriteLine("t2");
                num = 3;
            });
            t2.Start();
            t2.Wait();
            Console.WriteLine("线程执行完毕");
        });
    }

    运行:

    效果是实现了,代码看起来好搓啊,强迫症都犯了,没关系,可以使用更优雅的写法:

    private async void Test()
    {
        await Task.Run(async () =>
        {
            await Task.Delay(4000);
            Trace.WriteLine("第1个线程执行");
        });
        await Task.Run(async () =>
        {
            await Task.Delay(3000);
            Trace.WriteLine("第2个线程执行");
        });
        await Task.Run(async () =>
        {
            await Task.Delay(2000);
            Trace.WriteLine("第3个线程执行");
        });
    }

    运行:

    到此为止,功能就实现了,这个需求在Unity3d中使用协程很简单的几句就可以搞定,但在Winfrom等项目的开发中,确实有点繁琐。

    六、封装任务队列

    下面的代码我不认为是一个很好的写法,需要添加任务后,还得手动去调用,如果能添加到任务队列就不管了,让其自己自动按顺序来执行任务,岂不是更好,读者如果有兴趣自己去完善这个猜想。另外,在游戏开发中,比如RGP项目中,有专门的任务系统,它和我这个帖子的概念不能混为一谈,RPG任务系统更多的偏向数据的存取,来获取任务的完成状态。

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace Utils
    {
        public class TaskQueue
        {
            /// <summary>
            /// 任务列表
            /// </summary>
            private List<Task> TaskList = null;
            /// <summary>
            /// 是否在执行任务中
            /// </summary>
            private bool isPerformTask = false;
            /// <summary>
            /// 执行完任务的回调
            /// </summary>
            public Action CallBack = null;
    
    
            private static TaskQueue _instance = null;
            public static TaskQueue Instance
            {
                get
                {
                    if (_instance == null)
                        _instance = new TaskQueue();
                    return _instance;
                }
            }
    
            /// <summary>
            /// 添加任务
            /// </summary>
            /// <param name="task"></param>
            public void AddTask(Task task)
            {
                if (isPerformTask)
                {
                    Console.WriteLine("[TaskQueue]任务正在执行中,此时不能做赋值操作");
                    return;
                }
    
                if (task != null)
                {
                    TaskList.Add(task);
                }
            }
    
            /// <summary>
            /// 执行任务
            /// </summary>
            public void PerformTask()
            {
                if (isPerformTask)
                {
                    Console.WriteLine("[TaskQueue]任务正在执行中,不可重复调用");
                    return;
                }
                if (TaskList == null || TaskList.Count == 0)
                {
                    Console.WriteLine("[TaskQueue]任务列表为空");
                    return;
                }         
    
                Task.Run(() =>
                {
                    isPerformTask = true;
    
                    foreach (Task item in TaskList)
                    {
                        item.Start();
                        item.Wait();
                    }
    
                    TaskList.Clear();
                    isPerformTask = false;
    
                    if (CallBack != null) CallBack();
                });
            }
    
            private TaskQueue()
            {
                TaskList = new List<Task>();
            }
        }
    }
    

    调用:

    Task task1 = new Task(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("t1");
    });
    Task task2 = new Task(() =>
    {
        Thread.Sleep(2000);
        Console.WriteLine("t2");
    });
    Task task3 = new Task(() =>
    {
        Console.WriteLine("t3");
    });
    Action callback = () =>
    {
        Console.WriteLine("所有任务执行完成");
    };
    TaskQueue.Instance.AddTask(task1);
    TaskQueue.Instance.AddTask(task2);
    TaskQueue.Instance.AddTask(task3);
    TaskQueue.Instance.CallBack = callback;
    TaskQueue.Instance.PerformTask();

    运行:

    七、封装任务队列优化版

    2022.07.26 优化,和上面版本的区别是,任务添加完成后,会自动调用。

    执行任务队列,任务会一个个执行,中间可等待,并且不会阻塞主线程,在运行的时候,可以向任务队列中添加任务,不会影响任务的执行,

    代码:

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace Utils
    {
        public class TaskQueue
        {
            /// <summary>
            /// 任务队列
            /// </summary>
            private Queue<TaskData> QueuesTask = new Queue<TaskData>();
            /// <summary>
            /// 任务队列是否在执行中
            /// </summary>
            private bool isExecuteing = false;
    
            private static TaskQueue _instance = null;
            public static TaskQueue Instance
            {
                get
                {
                    if (_instance == null)
                        _instance = new TaskQueue();
                    return _instance;
                }
            }
    
            /// <summary>
            /// 任务是否进行中
            /// </summary>
            /// <returns></returns>
            public bool IsTasking()
            {
                return isExecuteing;
            }
    
            /// <summary>
            /// 添加任务,任务会按照队列自动执行
            /// </summary>
            /// <param name="task"></param>
            public void AddTaskAndRuning(TaskData taskData)
            {
                if (taskData == null) return;
    
                QueuesTask.Enqueue(taskData);
    
                StartPerformTask();
            } 
    
            /// <summary>
            /// 执行任务
            /// </summary>
            private async void StartPerformTask()
            {
                if (isExecuteing) { return; }
    
                while (QueuesTask.Count > 0)
                {
                    isExecuteing = true;
                    await Task.Run(() =>
                    {
                        TaskData taskDatas = QueuesTask.Dequeue();
                        Task task = taskDatas.Tasks;
                        task.Start();
                        task.Wait();
                        if (taskDatas.CallBack != null) taskDatas.CallBack(null);
                    });
                }
    
                isExecuteing = false;
            }
    
            private TaskQueue()
            {
            }
        }
    
        public class TaskData
        {
            /// <summary>
            /// 任务名
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 任务
            /// </summary>
            public Task Tasks { get; set; }
            /// <summary>
            /// 任务完成后的回调
            /// </summary>
            public Action<string> CallBack { get; set; }
        }
    }
    

    调用:

    TaskData taskData2 = new TaskData();
    taskData2.Tasks = new Task(() =>
    {
        Thread.Sleep(4000);
        Console.WriteLine("[Form1]taskData2");
    });
    taskData2.CallBack = (string res) =>
    {
        Console.WriteLine("[Form1]taskData2执行完的回调");
    };
    TaskQueue.Instance.AddTaskAndRuning(taskData2);
    
    
    TaskData taskData3 = new TaskData();
    taskData3.Tasks = new Task(() =>
    {
        Thread.Sleep(5000);
        Console.WriteLine("[Form1]taskData3");
    });
    taskData3.CallBack = (string res) =>
    {
        Console.WriteLine("[Form1]taskData3执行完的回调");
    };
    TaskQueue.Instance.AddTaskAndRuning(taskData3);

    运行:

    结束

    如果这个帖子对你有用,欢迎给我点赞 + 留言,谢谢

    end

    展开全文
  • 一篇看懂C#中的Task任务_初级篇

    千次阅读 2022-05-27 16:13:26
    文章目录一、前言二、认识2.1....Task.Factory.StartNew(() => { }); 还有, Task.Run(() => { });     于是,拿来一用,往花括号中填入一些业务代码。 “嗯,确实是多线程。” &nbs

    一、前言

        在.NET平台使用C#进行编程时,遇到了并行操作的需求,于是上网一搜多线程,一个非常简单语句出现了:

    Task.Factory.StartNew(() =>
    {
    
    });
    

    还有,

    Task.Run(() =>
    {
    });
    

        于是,拿来一用,往花括号中填入一些业务代码。
    “嗯,确实是多线程。”
        我在其他平台使用多线程的印象当中,使用多线程都是先建一个线程对象,然后往里面填许多参数,然后还要手动启停。

    	Thread thread = new Thread(p1, p2, func, 一堆看不懂的参数);
    	thread.Start();
    	...中间省略一万步
    	thread.Abort();
    

        相比之下,.net平台这种极简的多线程操作方法对新手真是友好。(毕竟是微软的嘛,非常善于对底层复杂应用抽象,到应用层程序员这一侧,只管用就行了)。
        随着项目的进行,并行操作越来越复杂,我也愈来愈觉得不能就这么简单一用就完事了,得稍微了解下它的特性以及使用时的注意项。

    二、认识

        于是上网搜了下C# Task使用,大部分都是翻译了一下微软官方文档里的东西(而且是自带的机翻)。那既然如此,我也试着结合官方文档来理解一下。
        下文出现的Task、task和任务基本上就是指一个东西,结合语境体会,不过一般首字母大写根据微软的习惯应该是表示类。

    2.1. 基于任务的多线程编程

        Task并行库(就是你用Task这个东西的相关库,全名Task Parallel Library,简称TPL)是基于Task这个概念的,它代表了一个并行操作。也确实,如果你对一个不玩编程的人说某个线程xxx,人家可能听不懂,线程明显是个专业术语嘛;如果说任务,是不是感觉更贴近现实了呢。
        某种程度上来说,一个task类似于thread(线程)或者ThreadPool工作项(线程池的一项嘛,还是线程),但是它处在一个更高的抽象层面上(毕竟本质上还是线程嘛?大概吧)。
        接着,文档还亲民地解释了一下,“并行任务”这个术语指的是一个或者多个独立的任务同时进行。
        Task提供了两点基本的好处:

    • 更效率且更灵活的使用系统资源。
      在背后呢(通常说在底层实现上),任务是排在线程池当中的,并且它用了一些算法(决策、调整线程数量和提供了负载均衡以加大吞吐量)进行增强。这使得任务相对轻量级,你可以创建许多任务使得并行操作更精细化。
    • 相比使用线程或者线程池,有更多的程序模块可以使用。
      任务和平台框架(指.NET这个平台吧)提供了丰富的API支持waiting, cancellation, continuations, robust exception handling, detailed status, custom scheduling等功能。

        因此,对于在.NET上使用多线程、异步操作和并行代码的你来说,TPL是更好的API(也就是说,在.NET下,微软非常建议你使用Task)。
        看完这一段,很明显感觉到微软在说它好用,但是底层实现上是一笔带过的,就讲了它底层是用了线程池那一套的,并且有所优化(我开始有点理解,为什么大家都说,微软的东西好入门,但是难深入了。因为确实好用易上手,但是细节屏蔽太多了)。

    2.2. 隐式地创建并运行一个任务

        Parallel.Invoke方法可以很便捷地让你同时运行任意数量的任意语句。仅仅需要为每个工作条目(item of work,或者说工作项)传递一个Action委托。创建这些委托最简单的方式是使用lambda表达式(这也是我一开始觉得task很简单的原因之一,因为不需要显示创建对象,用lambda就搞定了)。lambda表达式可以调用方法也可以添加代码行。下面的例子就展示了一个创建并启用两个同时运行任务的Invoke调用。

    	Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
    

    第一个任务表示通过lambda表达式调用了DoSomeWork方法,同理,第二个任务表示调用了DoSomeOtherWork。(温馨提示:如果你不了解lambda表达式,你可以先看看其他文章了解一下)。

    备注:
    由Invoke在底层创建的任务(Task)实例数量并不是一定与委托(Delegate)的数量相等的。
    TPL可能会使用各种优化,尤其当有大量委托的时候。
    

        上面是一个隐式使用任务的例子,为了更好地控制任务的执行,你应该更明确地使用任务对象。

    2.3. 显示地创建并运行一个任务

        用System.Threading.Tasks.Task类表示的任务不会返回值。用System.Threading.Tasks.Task<TResult>的任务会返回一个值,它继承自Task类。task对象处理基本的底层细节并提供了在任务生命周期内可由调用线程访问的方法和属性。例如,你可以在任意时刻访问任务的状态(Status)属性,以确定它是否在运行、是否运行完成、是否被取消或者是否抛出了异常。TaskStatus枚举描述了各种状态。
    任务状态

    2.3.1. Task常规使用

        当你创建一个任务时,你给它一个用户委托,该委托内封装了任务要执行的代码。这个委托可以是一个有命名的委托也可以是匿名的,还可以是lambda表达式。lambda表达式可以包含命名方法的调用,在下面的例子中予以展示。注意这个例子包含了一个Task.Wait方法的调用,用以确保任务在控制台模式结束前完成执行。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          Thread.CurrentThread.Name = "Main";
    
          // Create a task and supply a user delegate by using a lambda expression.
          Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
          // Start the task.
          taskA.Start();
    
          // Output a message from the calling thread.
          Console.WriteLine("Hello from thread '{0}'.",
                            Thread.CurrentThread.Name);
          taskA.Wait();
       }
    }
    // The example displays output like the following:
    //       Hello from thread 'Main'.
    //       Hello from taskA.
    

    2.3.2. Task.Run()

        你也可以使用Task.Run来一次性创建并启动一个任务。为了管理这个任务,Run方法使用了默认的任务调度器,无论这个任务调度器是否与当前线程相关。当你不需要对任务的创建和调度进行太多控制时,Run方法是更适用一些。(明显相对于上面的方式,Run将创建和启动合并为一步了)

    	Task taskA = Task.Run(() => Console.WriteLine("Hello from taskA."));
    

    2.3.3. Task.Factory.StartNew()

        你还可以使用TaskFactory.StartNew来一次性创建并启动一个任务。当创建和调度不必分离并且你需要额外的任务创建配置项或者使用特定的调度器,又或者你需要传递额外的状态到任务(你可以通过Task.AsyncState属性回收 )中时,你可以使用TaskFactory.StartNew方法。下面是一个例子:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CustomData
    {
    	public long CreationTime;
    	public int Name;
    	public int ThreadNum;
    }
    
    public class Example
    {
    	public static void Main()
    	{
    		Task[] taskArray = new Task[10];
    		for (int i = 0; i < taskArray.Length; i++)
    		{
    			taskArray[i] = Task.Factory.StartNew((object obj) => {
    				CustomData data = obj as CustomData;
    				if (data == null)
    					return;
    				data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
    			},
    			new CustomData(){Name = i, CreationTime = DateTime.Now.Ticks});
    		}
    		Task.WaitAll(taskArray);
    		foreach(var task in taskArray)
    		{
    			var data = task.AsyncState as CustomData;
    			if (data != null)
    				Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
    		}
    	}
    }
    // The example displays output like the following:
    //       Task #0 created at 635116412924597583 on thread #3.
    //       Task #1 created at 635116412924607584 on thread #4.
    //       Task #3 created at 635116412924607584 on thread #4.
    //       Task #4 created at 635116412924607584 on thread #4.
    //       Task #2 created at 635116412924607584 on thread #3.
    //       Task #6 created at 635116412924607584 on thread #3.
    //       Task #5 created at 635116412924607584 on thread #4.
    //       Task #8 created at 635116412924607584 on thread #4.
    //       Task #7 created at 635116412924607584 on thread #3.
    //       Task #9 created at 635116412924607584 on thread #4.
    

    2.3.4. 带有返回值的Task< TResult >

        Task和Task<TResult>都暴露了一个静态的Factory属性,该属性返回一个默认的TaskFactory实例,以便调用Task.Factory.StartNew()方法。同样,在下面的例子中,因为任务都是 System.Threading.Tasks.Task<TResult>类型的,它们都有一个 Task<TResult>.Result属性,该属性包含了运算的结果。任务是异步运行的,可能以任意时序执行完。若Result属性在运行结束前被访问,这个属性会阻塞调用线程直到该值可访问。

    using System;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
            Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                         Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
                                         Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };
    
            var results = new Double[taskArray.Length];
            Double sum = 0;
    
            for (int i = 0; i < taskArray.Length; i++) {
                results[i] = taskArray[i].Result;
                Console.Write("{0:N1} {1}", results[i],
                                  i == taskArray.Length - 1 ? "= " : "+ ");
                sum += results[i];
            }
            Console.WriteLine("{0:N1}", sum);
       }
    
       private static Double DoComputation(Double start)
       {
          Double sum = 0;
          for (var value = start; value <= start + 10; value += .1)
             sum += value;
    
          return sum;
       }
    }
    // The example displays the following output:
    //        606.0 + 10,605.0 + 100,495.0 = 111,706.0
    

    2.3.5. 结合lambda使用时出现的特殊情况

        当你使用lambda表达式创建一个委托时,你有权限访问在你源代码中此时可见的的所有变量。可是,在某些情况下,尤其是在循环中,一个lambda表达式无法如期捕获变量。它仅仅捕获了变量的引用,而不是每次迭代后发生变化的值(试着解释一下,任务的创建&启动需要时间大于循环执行完的事件,又因为任务委托中取得的是i的引用,所以委托真正执行时,循环已经执行完,引用去取出来的值都是最后的值了)。下面的例子说明了这个问题。它传递了一个循环计数器(int i)给lambda表达式,该表达式实例化了一个CustomData对象并使用了这个循环计数器作为对象的标识符。正如样例的输出展示的那样,每个CustomData对象都有完全一样的标识符,但这并不是你所期望的。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CustomData
    {
       public long CreationTime;
       public int Name;
       public int ThreadNum;
    }
    public class Example
    {
       public static void Main()
       {
          // Create the task object by using an Action(Of Object) to pass in the loop
          // counter. This produces an unexpected result.
          Task[] taskArray = new Task[10];
          for (int i = 0; i < taskArray.Length; i++) 
          {
             taskArray[i] = Task.Factory.StartNew( (Object obj) => {
             	var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks};
                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                	data.Name, data.CreationTime, data.ThreadNum);
             }, i);
          }
          Task.WaitAll(taskArray);
       }
    }
    // The example displays output like the following:
    //       Task #10 created at 635116418427727841 on thread #4.
    //       Task #10 created at 635116418427737842 on thread #4.
    //       Task #10 created at 635116418427737842 on thread #4.
    //       Task #10 created at 635116418427737842 on thread #4.
    //       Task #10 created at 635116418427737842 on thread #4.
    //       Task #10 created at 635116418427737842 on thread #4.
    //       Task #10 created at 635116418427727841 on thread #3.
    //       Task #10 created at 635116418427747843 on thread #3.
    //       Task #10 created at 635116418427747843 on thread #3.
    //       Task #10 created at 635116418427737842 on thread #4.
    

        在每次迭代时,你可以通过给任务的构造函数提供一个状态对象访问这该值。下面的样例程序通过在创建CustomData对象时使用循环计数器来修改了前面的例子,而CustomData对象又被传给了lambda表达式。正如样例输出的那样,现在每个CustomData对象都有唯一的基于循环计数器的标识符了。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CustomData
    {
       public long CreationTime;
       public int Name;
       public int ThreadNum;
    }
    public class Example
    {
       public static void Main()
       {
          // Create the task object by using an Action(Of Object) to pass in custom data
          // to the Task constructor. This is useful when you need to capture outer variables
          // from within a loop.
          Task[] taskArray = new Task[10];
          for (int i = 0; i < taskArray.Length; i++) {
             taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
             	CustomData data = obj as CustomData;
                if (data == null)
                	return;
                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                	data.Name, data.CreationTime, data.ThreadNum);
             },
             new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
          }
          Task.WaitAll(taskArray);
       }
    }
    // The example displays output like the following:
    //       Task #0 created at 635116412924597583 on thread #3.
    //       Task #1 created at 635116412924607584 on thread #4.
    //       Task #3 created at 635116412924607584 on thread #4.
    //       Task #4 created at 635116412924607584 on thread #4.
    //       Task #2 created at 635116412924607584 on thread #3.
    //       Task #6 created at 635116412924607584 on thread #3.
    //       Task #5 created at 635116412924607584 on thread #4.
    //       Task #8 created at 635116412924607584 on thread #4.
    //       Task #7 created at 635116412924607584 on thread #3.
    //       Task #9 created at 635116412924607584 on thread #4.
    

        该状态作为一个参数传给了任务的委托,并且它能够被任务对象用Task.AsyncState属性访问。下面示例是上一个示例的变种。它使用了AsyncState属性来显示被传入到lambda表达式中的CustomData对象的信息。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class CustomData
    {
       public long CreationTime;
       public int Name;
       public int ThreadNum;
    }
    public class Example
    {
       public static void Main()
       {
          Task[] taskArray = new Task[10];
          for (int i = 0; i < taskArray.Length; i++) 
          {
             taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
             	CustomData data = obj as CustomData;
                if (data == null)
                	return;
                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                },
                new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
          }
          Task.WaitAll(taskArray);
          foreach (var task in taskArray) {
             var data = task.AsyncState as CustomData;
             if (data != null)
                Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
          }
       }
    }
    // The example displays output like the following:
    //       Task #0 created at 635116412924597583 on thread #3.
    //       Task #1 created at 635116412924607584 on thread #4.
    //       Task #3 created at 635116412924607584 on thread #4.
    //       Task #4 created at 635116412924607584 on thread #4.
    //       Task #2 created at 635116412924607584 on thread #3.
    //       Task #6 created at 635116412924607584 on thread #3.
    //       Task #5 created at 635116412924607584 on thread #4.
    //       Task #8 created at 635116412924607584 on thread #4.
    //       Task #7 created at 635116412924607584 on thread #3.
    //       Task #9 created at 635116412924607584 on thread #4.
    

    三、总结

        归纳一下,task的几个特点:

    • 底层是在线程池中运作的,且微软用了一些增强性能的算法(这点使你在大部分场景中可以无忧地使用它)
    • 实际使用往往会结合lambda表达式,正如网上搜到的那样 task(()=>{Your code}))
    • 结合lambda表达式在循环中使用计数器时,需要注意lambda内使用计数器是其引用。可以使用自定义传参给任务来回避这种情况。

        看了以上几个例子后,对task有个大致的了解,并且基本使用应该是没有太大问题了。但是文档往下翻,还有许多详细参数,细节方面的问题。必要时进行深入学习。

    四、参考资料

    微软文档

    展开全文
  • Task用法详解

    千次阅读 2020-12-31 15:42:19
    Task的创建和运行,Task有如下三种方法创建 //Task的创建与运行 static void Main(string[] args) { //1.new 方式实例化一个Task,需要通过Start方法启动 Task task=new Task(()=> { Thread.Sleep(1000); ...
  • TaskScheduler的核心任务是提交TaskSet到集群运算并汇报结果。 为TaskSet创建和维护一个TaskSetManager, 并追踪任务的本地性及错误信息。 遇到Straggle任务会放到其他结点进行重试。 向DAGScheduler汇报执行情况, ...
  • 深入理解gradle中的task

    千次阅读 2021-02-15 08:56:04
    在之前的文章中,我们讲到了如何使用gradle创建一个简单的task,以及task之间怎么依赖,甚至使用了程序来创建task。在本文中,我们会更加深入的去了解一下gradle中的task
  • Android Gradle Task详解

    万次阅读 2019-04-16 21:06:57
    本文介绍如何创建一个task,如何为自定义和现有的task的添加action。以及如何配置task之间的依赖。
  • c#中使用Task

    千次阅读 2022-04-15 16:35:00
    可以先看前面的backgroudwork文章,再看Thread文章,本demo接着Thread的环境继续。 本demo演示的是: Task有三种写法,喜欢那种就选择那种。 并且在Task中进行停止,暂停,... var task1 = new Task(() => .....
  • C++11中std::packaged_task的使用

    千次阅读 2020-01-31 19:53:45
    C++11中的std::packaged_task是个模板类。std::packaged_task包装任何可调用目标(函数、lambda表达式、bind表达式、函数对象)以便它可以被异步调用。它的返回值或抛出的异常被存储于能通过std::future对象访问的共享...
  • C#任务Task应用详解

    千次阅读 2022-04-28 10:21:34
    System.Threading.Task 2、定义 Task类可以更好的控制并行操作,任务表示将完成的某个工作单元,该工作单元可以在单独的线程运行,可以以同步方式启动一个新任务。 3、启动任务的方式 任务处理程序 public static ...
  • Spark中Task数量的分析

    千次阅读 2020-02-29 20:39:39
    本文主要说一下Spark中Task相关概念、RDD计算时Task的数量、Spark Streaming计算时Task的数量。 Task作为Spark作业执行的最小单位,Task的数量及运行快慢间接决定了作业运行的快慢。 开始 先说明一下Spark作业的几...
  • Task.CompletedTaskTask.Result小记

    千次阅读 2020-12-26 19:28:56
    学习中间件出现一个知识,如下写法,一开始不太懂 中间件三部曲: 定义接口... public Task Do() { //逻辑代码 return Task.CompletedTask; } public Task DoString() { //逻辑代码 return Task.FromResult("aaa"); }
  • C# Task 及 async Task

    千次阅读 2021-04-05 21:41:36
    使用 async task 尝试 , 异步方法本身不会节省 请求时间,但可以减轻主线程压力; 异步方法内默认不会开启新线程(语法可通过),除非手动开启;通常手写异步方法需要task.run 开启新新增, 语法才能保证正确。...
  • C# Task的各种用法和详解(推荐,精)

    万次阅读 多人点赞 2019-10-25 13:42:11
    1、Task的优势  ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:  ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;  ◆ ThreadPool不支持线程执行的...
  • celery中task和share_task的区别

    千次阅读 2022-03-06 12:55:58
    task: 装饰函数,将函数当成celery的任务函数 import time from celery import Celery app = Celery('tasks',broker='redis://192.168.10.48:6379',backend='redis://192.168.10 .48:6379') @app.task def ...
  • C# Task和async/await详解

    万次阅读 多人点赞 2019-07-30 15:04:09
    C# Task和async/await详解什么是异步Task介绍1 Task创建和运行2 Task的阻塞方法(Wait/WaitAll/WaitAny)1 Thread阻塞线程的方法如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一...
  • #include "freertos/task.h" #include "event_source.h" #include "esp_event_base.h" static const char* TAG = "user_event_loops"; //事件循环 esp_event_loop_handle_t loop_with_task; //循环带任务 esp_...
  • C#Task执行线程及其相关问题

    万次阅读 2018-06-01 18:54:38
    在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务...
  • 任务调度器TaskScheduler

    千次阅读 2019-03-25 09:54:42
    TaskScheduler可以看做任务调度的客户端,负责任务的提交,并且请求集群管理器对任务调度。TaskScheduler的类UML图如下,针对不同部署方式会有不同的TaskScheduler与SchedulerBackend进行组合。TaskScheduler类负责...
  • Android 项目使用 Jenkins 打包时的异常...Execution failed for task ':app:mergeDebugResources'. > Multiple task action failures occurred: > A failure occurred while executing com.android.build.gra..
  • C# task 取消

    万次阅读 2018-12-24 10:27:46
     我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。  在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的...
  • 多线程之Task(任务)

    万次阅读 多人点赞 2018-11-12 15:44:30
    一、介绍下Task 对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程, 然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task...
  • Gradle之task的使用

    万次阅读 2018-09-17 10:03:35
    在上一篇文章中(https://blog.csdn.net/weixin_38062353/article/details/82230239)做了对gradle的基本介绍,本篇文章将探索最基础的gradle构建块:project和task以及它们和API之间的映射。 一、构建块。 每一...
  • c#之task与thread区别及其使用

    千次阅读 2020-02-10 20:43:19
    通过wait()对单个task进行等待,Task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。 4.task的回调执行  var testTask = new Task(() =>  {  Console.WriteLine("task start")...
  • 【cpp-taskflow】源码分析

    千次阅读 2020-02-07 01:07:08
    taskflow一个写的比较好的基于task有向无环图(DAG)的并行调度的框架,之所以说写的比较好,个人觉得有几点原因: 1.是一个兼具学术研究和工业使用的项目,并非一个玩具 2.现代C++开发,风格简洁 (源码要求编译器...
  • C# Task 暂停与取消

    千次阅读 2021-12-25 01:38:00
    前言:①取消task任务之CancellationTokenSource的用法;②task的线程管控方法Task..Wait(time),Task.WaitAll(),Task.Wait...
  • Gradle基础:5:task的依赖与执行条件

    万次阅读 2018-11-21 06:58:39
    在使用maven的时候,compile/test/install的先后顺序和依赖关系都十分清晰,而在前面的例子中,可以通过gradle compile和gradle test来进行不同的任务执行,但是实际...这篇文章会继续来介绍task之间的依赖与执行条件。
  • C#Task的创建和Wait等待

    千次阅读 2021-09-28 14:18:13
    假设有一个异步方法taskDo,返回一个Task类型。当我们用任务执行该异步方法,且需等待该方法执行完成时,应该如何创建任务呢??? static void WriteLine(object line) { Console.WriteLine(DateTime.Now....
  • Task和thread区别和使用

    千次阅读 2020-07-14 21:12:20
    c#之task与thread区别及其使用 1.什么是thread 当我们提及多线程的时候会想到thread和threadpool,这都是异步操作,threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,141,129
精华内容 456,451
关键字:

task

友情链接: CCD-sampling.rar