bwin必赢棋牌使用任务并行库,多线程编程系列

bwin必赢棋牌 8

目录

目录

  • 系列大纲
  • 一、前言
  • 二、目录结构
  • 四、章节结构
  • 五、相关链接
  • 1.1
    简介
  • 1.2
    创建任务
  • 1.3
    使用任务执行基本的操作
  • 1.4
    组合任务
  • 1.5
    将APM模式转换为任务
  • 1.6
    将EAP模式转换为任务
  • 1.7
    实现取消选项
  • bwin必赢棋牌,1.8
    处理任务中的异常
  • 1.9
    并行运行任务
  • 1.10
    使用TaskScheduler配置任务执行
  • 参考书籍
  • 笔者水平有限,如果错误欢迎各位批评指正!



本系列首页链接:[C#多线程编程系列(一)-
简介 ]

系列大纲

目前只整理到第二章,线程同步,笔者后面会慢慢更新,争取能把这本书中精华的知识都分享出来。
C#多线程编程系列(一)-
简介
C#多线程编程系列(二)-
线程基础
C#多线程编程系列(三)-
线程同步
C#多线程编程系列(四)-
使用线程池
C#多线程编程系列(五)-
使用任务并行库

源码下载点击链接
示例源码下载


一、前言

在C#学习过程中,多线程一直都是比较难的部分,因为其中涉及到很多与操作系统相关的知识。比如:如何进行多线程编程、线程同步、线程锁、线程异步、并行编程、并行集合等等的知识。所以笔者在学习过程中也是遇到了重重困难,而且一直没有好的教程。

但是笔者在浏览GitHub时,发现有大佬已经推荐了一本新书,《MULTITHREADING
WITH C# COOKBOOK SECOND
EDITION》
,其中主要就是讲如何在C#中使用多线程的。看到这本书笔者是如获珍宝,终于能有机会系统的学习多线程相关的知识了。

于是便有了这一个开篇,这个更多的是阅读这本书的笔记和一些按照书本上写的范例程序,当然也会有一些自己的思考。

1.1 简介

在之前的几个章节中,就线程的使用和多线程相关的内容进行了介绍。因为线程涉及到异步、同步、异常传递等问题,所以在项目中使用多线程的代价是比较高昂的,需要编写大量的代码来达到正确性和健壮性。

为了解决这样一些的问题,在.Net Framework 4.0中引入了一个关于一步操作的API。它叫做任务并行库(Task
Parallel
Library)
。然后在.Net Framwork 4.5中对它进行了轻微的改进,本文的案例都是用最新版本的TPL库,而且我们还可以使用C#
5.0的新特性await/async来简化TAP编程,当然这是之后才介绍的。

TPL内部使用了线程池,但是效率更高。在把线程归还回线程池之前,它会在同一线程中顺序执行多少Task,这样避免了一些小任务上下文切换浪费时间片的问题。

任务是对象,其中封装了以异步方式执行的工作,但是委托也是封装了代码的对象。任务和委托的区别在于,委托是同步的,而任务是异步的。

在本章中,我们将会讨论如何使用TPL库来进行任务之间的组合同步,如何将遗留的APM和EAP模式转换为TPL模式等等。

二、目录结构

本书一共分为十一个章节,分别从线程基础、线程同步、线程池、Task并行库、C#
6.0特性、并发集合类、PLINQ、反应式编程、异步I/O、并行变成模式和在UWP
.Net Core中应用来完成的介绍了C#多线程编程。如下图所示。

bwin必赢棋牌 1

附上百度脑图链接

笔者认为本书真的是一本不可多得的好书,回想起那段被多线程虐过的日子。粗略的过了一遍以后就打算马上拿出来分享给大家,后文有相关的购买链接,大家也可以直接在某宝、某东搜索关键字,价格也是比较便宜的,多多支持正版。

1.2 创建任务

在本节中,主要是演示了如何创建一个任务。其主要用到了System.Threading.Tasks命名空间下的Task类。该类可以被实例化并且提供了一组静态方法,可以方便快捷的创建任务。

在下面实例代码中,分别延时了三种常见的任务创建方式,并且创建任务是可以指定任务创建的选项,从而达到最优的创建方式。

TaskCreationOptions中一共有7个枚举,枚举是可以使用|运算符组合定义的。其枚举如下表所示。

成员名称 说明
AttachedToParent 指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关详细信息,请参阅附加和分离的子任务
DenyChildAttach 指定任何尝试作为附加的子任务执行(即,使用 AttachedToParent 选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
HideScheduler 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 Default 当前计划程序。
LongRunning 指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 TaskScheduler 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。
None 指定应使用默认行为。
PreferFairness 提示 TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
RunContinuationsAsynchronously 强制异步执行添加到当前任务的延续任务。请注意,RunContinuationsAsynchronously 成员在以 .NET Framework 4.6 开头的 TaskCreationOptions 枚举中可用。
static void Main(string[] args)
{
    // 使用构造方法创建任务
    var t1 = new Task(() => TaskMethod("Task 1"));
    var t2 = new Task(() => TaskMethod("Task 2"));

    // 需要手动启动
    t2.Start();
    t1.Start();

    // 使用Task.Run 方法启动任务  不需要手动启动
    Task.Run(() => TaskMethod("Task 3"));

    // 使用 Task.Factory.StartNew方法 启动任务 实际上就是Task.Run
    Task.Factory.StartNew(() => TaskMethod("Task 4"));

    // 在StartNew的基础上 添加 TaskCreationOptions.LongRunning 告诉 Factory该任务需要长时间运行
    // 那么它就会可能会创建一个 非线程池线程来执行任务  
    Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);

    ReadLine();
}

static void TaskMethod(string name)
{
    WriteLine($"任务 {name} 运行,线程 id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}.");
}

运行结果如下图所示。

bwin必赢棋牌 2

四、章节结构

本书主要是偏实践应用一些,其中每个章节中的技术说明都分为三个部分,准备工作(Getting
ready)、实现方式(How to do it…)和实现原理(How it works…)

本文节节选第一章的第一小节举例,首先是准备工作。

1.准备工作

bwin必赢棋牌 3

2.实现方式

bwin必赢棋牌 4bwin必赢棋牌 5

3.实现原理

bwin必赢棋牌 6

国外的书一般都是比较偏理论,像这种理论和实践结合的还是比较少,所以第一时间推荐给大家。

1.3 使用任务执行基本的操作

在本节中,使用任务执行基本的操作,并且获取任务执行完成后的结果值。本节内容比较简单,在此不做过多介绍。

演示代码如下,在主线程中要获取结果值,常用的方式就是访问task.Result属性,如果任务线程还没执行完毕,那么会阻塞主线程,直到线程执行完。如果任务线程执行完毕,那么将直接拿到运算的结果值。

Task 3中,使用了task.Status来打印线程的状态,线程每个状态的具体含义,将在下一节中介绍。

static void Main(string[] args)
{
    // 直接执行方法 作为参照
    TaskMethod("主线程任务");

    // 访问 Result属性 达到运行结果
    Task<int> task = CreateTask("Task 1");
    task.Start();
    int result = task.Result;
    WriteLine($"运算结果: {result}");

    // 使用当前线程,同步执行任务
    task = CreateTask("Task 2");
    task.RunSynchronously();
    result = task.Result;
    WriteLine($"运算结果:{result}");

    // 通过循环等待 获取运行结果
    task = CreateTask("Task 3");
    WriteLine(task.Status);
    task.Start();

    while (!task.IsCompleted)
    {
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(0.5));
    }

    WriteLine(task.Status);
    result = task.Result;
    WriteLine($"运算结果:{result}");

    Console.ReadLine();
}

static Task<int> CreateTask(string name)
{
    return new Task<int>(() => TaskMethod(name));
}

static int TaskMethod(string name)
{
    WriteLine($"{name} 运行在线程 {CurrentThread.ManagedThreadId}上. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(2));

    return 42;
}

运行结果如下,可见Task 1
Task 2均是运行在主线程上,并非线程池线程。

bwin必赢棋牌 7

五、相关链接

附上购买地址,大家还是多多支持正版.

《MULTITHREADING WITH C# COOKBOOK SECOND
EDITION》购买地址

补充,本书有中文翻译版本,由黄博文大佬翻译,但是好像还是第一版。

《C#多线程编程实战》购买地址

1.4 组合任务

在本节中,体现了任务其中一个强大的功能,那就是组合任务。通过组合任务可很好的描述任务与任务之间的异步、同步关系,大大降低了编程的难度。

组合任务主要是通过task.ContinueWith()task.WhenAny()task.WhenAll()等和task.GetAwaiter().OnCompleted()方法来实现。

在使用task.ContinueWith()方法时,需要注意它也可传递一系列的枚举选项TaskContinuationOptions,该枚举选项和TaskCreationOptions类似,其具体定义如下表所示。

成员名称 说明
AttachedToParent 如果延续为子任务,则指定将延续附加到任务层次结构中的父级。 只有当延续前面的任务也是子任务时,延续才可以是子任务。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关更多信息,请参见Attached and Detached Child Tasks
DenyChildAttach 指定任何使用 TaskCreationOptions.AttachedToParent 选项创建,并尝试作为附加的子任务执行的子任务(即,由此延续创建的任何嵌套内部任务)都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
ExecuteSynchronously 指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。 如果前面任务的 CancellationTokenSource 已在一个 finally(在 Visual Basic 中为 Finally)块中释放,则使用此选项的延续任务将在该 finally 块中运行。 只应同步执行运行时间非常短的延续任务。由于任务以同步方式执行,因此无需调用诸如 Task.Wait 的方法来确保调用线程等待任务完成。
HideScheduler 指定由延续通过调用方法(如 Task.RunTask.ContinueWith)创建的任务将默认计划程序 (TaskScheduler.Default) 视为当前的计划程序,而不是正在运行该延续的计划程序。
LazyCancellation 在延续取消的情况下,防止延续的完成直到完成先前的任务。
LongRunning 指定延续将是长期运行的、粗粒度的操作。 它会向 TaskScheduler 提示,过度订阅可能是合理的。
None 如果未指定延续选项,应在执行延续任务时使用指定的默认行为。 延续任务在前面的任务完成后以异步方式运行,与前面任务最终的 Task.Status 属性值无关。 如果延续为子任务,则会将其创建为分离的嵌套任务。
NotOnCanceled 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
NotOnFaulted 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。 此选项对多任务延续无效。
NotOnRanToCompletion 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
OnlyOnCanceled 指定只应在延续前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
OnlyOnFaulted 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。OnlyOnFaulted 选项可保证前面任务中的 Task.Exception 属性不是 null。 你可以使用该属性来捕获异常,并确定导致任务出错的异常。 如果你不访问 Exception 属性,则不会处理异常。 此外,如果尝试访问已取消或出错的任务的 Result 属性,则会引发一个新异常。此选项对多任务延续无效。
OnlyOnRanToCompletion 指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
PreferFairness 提示 TaskScheduler 按任务计划的顺序安排任务,因此较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
RunContinuationsAsynchronously 指定应异步运行延续任务。 此选项优先于 TaskContinuationOptions.ExecuteSynchronously。

演示代码如下所示,使用ContinueWith()OnCompleted()方法组合了任务来运行,搭配不同的TaskCreationOptionsTaskContinuationOptions来实现不同的效果。

static void Main(string[] args)
{
    WriteLine($"主线程 线程 Id {CurrentThread.ManagedThreadId}");

    // 创建两个任务
    var firstTask = new Task<int>(() => TaskMethod("Frist Task",3));
    var secondTask = new Task<int>(()=> TaskMethod("Second Task",2));

    // 在默认的情况下 ContiueWith会在前面任务运行后再运行
    firstTask.ContinueWith(t => WriteLine($"第一次运行答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}"));

    // 启动任务
    firstTask.Start();
    secondTask.Start();

    Sleep(TimeSpan.FromSeconds(4));

    // 这里会紧接着 Second Task运行后运行, 但是由于添加了 OnlyOnRanToCompletion 和 ExecuteSynchronously 所以会由运行SecondTask的线程来 运行这个任务
    Task continuation = secondTask.ContinueWith(t => WriteLine($"第二次运行的答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"),TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);

    // OnCompleted 是一个事件  当contiuation运行完成后 执行OnCompleted Action事件
    continuation.GetAwaiter().OnCompleted(() => WriteLine($"后继任务完成. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}"));

    Sleep(TimeSpan.FromSeconds(2));
    WriteLine();

    firstTask = new Task<int>(() => 
    {
        // 使用了TaskCreationOptions.AttachedToParent 将这个Task和父Task关联, 当这个Task没有结束时  父Task 状态为 WaitingForChildrenToComplete
        var innerTask = Task.Factory.StartNew(() => TaskMethod("Second Task",5), TaskCreationOptions.AttachedToParent);

        innerTask.ContinueWith(t => TaskMethod("Thrid Task", 2), TaskContinuationOptions.AttachedToParent);

        return TaskMethod("First Task",2);
    });

    firstTask.Start();

    // 检查firstTask线程状态  根据上面的分析 首先是  Running -> WatingForChildrenToComplete -> RanToCompletion
    while (! firstTask.IsCompleted)
    {
        WriteLine(firstTask.Status);

        Sleep(TimeSpan.FromSeconds(0.5));
    }

    WriteLine(firstTask.Status);

    Console.ReadLine();
}

static int TaskMethod(string name, int seconds)
{
    WriteLine($"任务 {name} 正在运行,线程池线程 Id {CurrentThread.ManagedThreadId},是否为线程池线程: {CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(seconds));

    return 42 * seconds;
}

运行结果如下图所示,与预期结果一致。其中使用了task.Status来打印任务运行的状态,对于task.Status的状态具体含义如下表所示。

成员名称 说明
Canceled 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。 有关详细信息,请参阅任务取消
Created 该任务已初始化,但尚未被计划。
Faulted 由于未处理异常的原因而完成的任务。
RanToCompletion 已成功完成执行的任务。
Running 该任务正在运行,但尚未完成。
WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成。
WaitingToRun 该任务已被计划执行,但尚未开始执行。

bwin必赢棋牌 8

发表评论

电子邮件地址不会被公开。 必填项已用*标注