C#Task 学习 一

本文介绍了.NET4中新增的System.Threading.Tasks名称空间,并详细解释了如何使用Task类来创建和管理后台线程任务。包括任务的启动方式、连续任务的创建及任务层次结构的概念。

 .NET 4 中 包含了新名称空间System.Threading.Task。它包含的类抽象出了线程的功能。使用Task类创建的任务是后台线程,所以在前台线程全部终止的时候,如果任务还没有全部执行万,就会被被动终止。

启动任务

  怎样启动一个任务?代码中我们首先要添加using System.Threading.Tasks;引用。我们可以使用TaskFactory类或Task类的构造函数和Start()方法。在启动任务时,会创建Task类的一个实例。首先我们看一段代码

using System;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleTask
{
    class Program
    {
        static void Main(string[] args)
        {
            //使用TaskFactory创建一个任务
            TaskFactory tf = new TaskFactory();
            Task t1 = tf.StartNew(NewTask);
            //使用Task类de Factory创建一个任务
            Task t2 = Task.Factory.StartNew(NewTask);
            ///////////////////////////////////////
            Task t3 = new Task(NewTask);
            t3.Start();
            Task t4 = new Task(NewTask, TaskCreationOptions.PreferFairness);
            t4.Start();
            Thread.Sleep(1000);//因为任务是后台线程,所以我们这里阻塞主线程一秒钟来等待任务全部执行完成
        }
        static void NewTask()
        {
            Console.WriteLine("开始一个任务");
            Console.WriteLine("Task id:{0}",Task.CurrentId);
            Console.WriteLine("任务执行完成");
        }
    }
}

运行结果:

  在上面代码中。我们可以看到启动新任务的不同方式:

  1、  第一种使用实例化的TaskFactory类,把NewTask()方法传递给StartNew()方法,任务就会立即启动。

  2、  第二种方式是使用Task类的构造函数,实例化任务时,任务不会立即启动,此时任务的状态为Created。然后调用Task类的Start()方法来启动任务,还可以调用RunSynchronously()方法来启动任务。

默认情况下,任务是异步运行的。。。

  使用Task类的构造函数和TaskFactory类的StartNew()方法时,都可以传递TaskCreationOptions枚举中的值。

成员名称

说明

None

指定应使用默认行为。

PreferFairness

提示 TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。

LongRunning

指定某个任务将是运行时间长、粗粒度的操作。 它会向TaskScheduler 提示,过度订阅可能是合理的。

AttachedToParent

指定将任务附加到任务层次结构中的某个父级。

  注:摘录至MSDN:http://technet.microsoft.com/zh-CN/library/system.threading.tasks.taskcreationoptions

  例如:如果任务使用子任务创建了其它任务,子任务就优先于其它任务。当然它们不会排在线程池的最后。如果这些任务应以公平的方式与所有其它任务一起处理,就设置选项为PerferFainress,如下所示

Task t4 = new Task(NewTask,TaskCreationOptions.PreferFairness);
t4.Start();

创建连续的任务

  通过创建任务,我们可以指定在一个任务完成过后,开始运行另外一个指定的任务。任务处理程序不带参数或者带一个Object类型的参数。连续处理程序有一个Task类型的参数,可以访问起始任务的相关信息。

using System;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleContinueTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t1 = new Task(FirstTask);
            Task t2 = t1.ContinueWith(SecondTask);
            t1.Start();
            Thread.Sleep(7000);
        }
        static void FirstTask()
        {
            Console.WriteLine("第一个任务开始:TaskID:{0}",Task.CurrentId);
            Thread.Sleep(3000);
        }
        static void SecondTask(Task task)
        {
            Console.WriteLine("任务{0}完成",task.Id);
            Console.WriteLine("第二个任务开始:TaskID:{0}",Task.CurrentId);
            Console.WriteLine("清理工作......");
            Thread.Sleep(3000);
        }
    }

}

  运行结果:

  

  连续的任务通过在任务上调用ContinueWith()方法来定义。t1.ContinueWith(SecondTask)方法表示,调用SecondTask()方法的新任务应该在t1任务完成后立即启动过,在第一个任务结束时,还可以启动多个任务。无论前一个任务是如何结束的,前面的连续任务总是在前面一个任务结束时启动,使用TaskContinueOptions枚举中的值可以指定连续任务只有在起始任务成功或者失败时启动,TaskContinueOptions的枚举值有

成员名称

说明

None

Default = "Continue on any, no task options, run asynchronously" 指定应使用默认行为。 默认情况下,完成前面的任务之后将安排运行延续任务,而不考虑前面任务的最终TaskStatus

PreferFairness

提示 TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。

LongRunning

指定某个任务将是运行时间长、粗粒度的操作。 它会向TaskScheduler 提示,过度订阅可能是合理的。

AttachedToParent

指定将任务附加到任务层次结构中的某个父级。

NotOnRanToCompletion

指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 此选项对多任务延续无效。

NotOnFaulted

指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 此选项对多任务延续无效。

NotOnCanceled

指定不应在延续任务前面的任务已取消的情况下安排延续任务。 此选项对多任务延续无效。

OnlyOnRanToCompletion

指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 此选项对多任务延续无效。

OnlyOnFaulted

指定只应在延续任务前面的任务引发了未处理异常的情况下才安排延续任务。 此选项对多任务延续无效。

OnlyOnCanceled

指定只应在延续任务前面的任务已取消的情况下才安排延续任务。 此选项对多任务延续无效。

ExecuteSynchronously

指定应同步执行延续任务。 指定此选项后,延续任务将在导致前面的任务转换为其最终状态的相同线程上运行。 如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。 只应同步执行运行时间非常短的延续任务。

  摘录至MSDN:http://technet.microsoft.com/zh-CN/library/system.threading.tasks.taskcontinuationoptions

任务的层次结构

  利用任务的连续性,就可以在一个任务结束后启动另外一个任务。一个任务启动一个新任务时,就启动了一个父/子层次结构,如下所示:

using System;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleLayer
{
    class Program
    {
        static void Main(string[] args)
        {
            Task parent = new Task(ParentTask);
            parent.Start();
            Thread.Sleep(1000);
            Console.WriteLine("父任务的状态:{0}",parent.Status);
            Thread.Sleep(4000);
            Console.WriteLine("父任务的状态:{0}",parent.Status);
        }
        public static void ParentTask()
        {
            Console.WriteLine("父任务{0}正在运行......",Task.CurrentId);
            Task child = new Task(ChildTask);
            child.Start();
            Console.WriteLine("父任务启动了子任务");
            Thread.Sleep(1000);
        }
        static void ChildTask()
        {
            Console.WriteLine("子任务{0}正在运行......",Task.CurrentId);
            Thread.Sleep(3000);
            Console.WriteLine("子任务执行完成。");
        }
    }
}

运行结果:

  如果父任务在子任务之前结束,父任务的状态就会显示为WatingForChildrenToComplete。子任务的任务完成后,父任务的状态就是RanToCompletion。

03-18
<think>好的,我现在需要帮助用户了解C#中Task的使用方法,特别是异步编程方面的示例。根据用户提供的引用内容,我需要整理相关信息,并确保回答结构清晰,符合用户的要求。 首先,用户提到了四个引用,其中引用[1]讨论了在Main方法中使用async Task,引用[2]涉及TPL和Task.WhenAll的用法,引用[3]展示了Task.Run和Result属性的使用,引用[4]解释了如何通过Result获取异步结果。这些都是Task的关键点,我需要将这些内容整合到回答中。 接下来,用户要求回答结构清晰,分步骤解决问题,并尽量真实可靠。因此,我应该将回答分成几个主要部分,比如基础用法、异步方法定义、任务组合、错误处理等,每个部分都给出示例代码,并引用对应的来源。 需要注意的是,用户希望示例代码使用中文注释,这可能更符合他们的阅读习惯。此外,必须使用正确的LaTeX格式,尽管问题中数学内容不多,但如果有公式的话要正确应用。不过当前问题主要集中在代码示例上,可能不需要数学公式。 然后,我需要确保每个示例都正确无误,并且与引用的内容对应。例如,引用[1]中的Main方法使用async Task,引用[3]中的Task.Run和Result属性,引用[2]中的Task.WhenAll,引用[4]中的Result阻塞问题。同时,在错误处理部分,可能需要补充如何捕获异常,这在用户提供的引用中有直接提到,但属于Task使用的常见问题,应该包含进去。 最后,生成相关问题,需要围绕Task的更多方面,比如优化、异常处理、取消任务等,确保问题相关且有深度,帮助用户进学习。 检查是否有遗漏的部分,比如是否覆盖了基础用法、组合任务、错误处理,以及是否引用了正确的来源。确保代码示例正确,特别是异步方法的定义和调用,避免语法错误。例如,在示例1中,Main方法需要是async Task,而在控制台应用中需要C# 7.1及以上版本支持,可能需要提醒用户,但用户提供的引用[1]已经提到这点,所以可以省略。 现在,组织这些内容,确保逻辑连贯,每个部分都有对应的引用标注,并且问题部分涵盖可能的扩展方向。</think>### C# Task 使用方法与异步编程示例 在 C# 中,`Task` 是异步编程的核心类型,用于表示异步操作。以下是其关键用法及示例: --- #### 1. **基础用法:启动异步任务** 通过 `Task.Run` 可在线程池中执行耗时操作,避免主线程阻塞。 **示例**(引用[3][^3]): ```csharp using System; using System.Threading.Tasks; class Program { static void Main(string[] args) { Task<int> task = Task.Run(() => { Console.WriteLine("正在计算..."); return 2024; // 返回计算结果 }); int result = task.Result; // 等待并获取结果 Console.WriteLine($"结果:{result}"); } } ``` - **说明**:`Task.Run` 将代码块提交到线程池,`Result` 属性会阻塞当前线程直到任务完成[^4]。 --- #### 2. **定义异步方法** 使用 `async`/`await` 关键字定义异步方法,提升代码可读性。 **示例**(引用[2][^2]): ```csharp public async Task<int> CalculateSumAsync(int a, int b) { await Task.Delay(1000); // 模拟耗时操作 return a + b; } // 调用方法 public static async Task Main(string[] args) { int sum = await new MyClass().CalculateSumAsync(3, 5); Console.WriteLine($"和为:{sum}"); } ``` - **说明**:`await` 会挂起当前方法,将控制权交回调用方,直到任务完成[^1]。 --- #### 3. **组合多个任务** 使用 `Task.WhenAll` 或 `Task.WhenAny` 并行执行多个任务。 **示例**(引用[2]): ```csharp public async Task ProcessDataAsync() { var tasks = new List<Task>(); for (int i = 0; i < 10; i++) { int id = i; tasks.Add(Task.Run(() => PerformOperation(id))); // 并行执行任务 } await Task.WhenAll(tasks); // 等待所有任务完成 } private void PerformOperation(int id) { Console.WriteLine($"处理任务 {id}"); } ``` - **说明**:`Task.WhenAll` 返回个任务,该任务在所有输入任务完成后完成。 --- #### 4. **错误处理** 通过 `try-catch` 捕获异步操作中的异常。 **示例**: ```csharp public async Task HandleErrorsAsync() { try { await Task.Run(() => { throw new InvalidOperationException("模拟错误"); }); } catch (Exception ex) { Console.WriteLine($"捕获异常:{ex.Message}"); } } ``` - **说明**:异步方法抛出的异常会传播到 `await` 表达式的调用处[^4]。 --- #### 5. **避免阻塞** 优先使用 `await` 替代 `Result` 或 `Wait()`,防止死锁。 **错误示例**(引用[4]): ```csharp var task = CalculateSumAsync(2, 3); int result = task.Result; // 在同步上下文中可能引发死锁 ``` - **正确做法**:在异步方法中始终使用 `await`。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值