图片 8

多线程编程系列

目录

目录

  • 多种大纲
  • 一、前言
  • 二、目录结构
  • 四、章节结构
  • 五、相关链接
  • 1.1
    简介
  • 1.2
    创制任务
  • 1.3
    使用职责实践基本的操作
  • 1.4
    组合义务
  • 1.5
    将APM情势转换为天职
  • 1.6
    将EAP形式转变为任务
  • 1.7
    达成打消选项
  • 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#多线程编制程序。如下图所示。

图片 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}.");
}

运营结果如下图所示。

图片 2

四、章节结构

本书首如若偏实行应用有的,当中各样章节中的本领验证都分为多少个部分,策画干活(Getting
ready)、实现方式(How to do it…)和完成原理(How it works…)

正文节节选第一章的率先小节例如,首先是希图专业。

1.预备工作

图片 3

2.贯彻情势

图片 4图片 5

3.落实原理

图片 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均是运作在主线程上,并非线程池线程。

图片 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 该任务已被计划执行,但尚未开始执行。

图片 8

发表评论

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