金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > 编程 > 使用任务并行库,构造函数赋值

使用任务并行库,构造函数赋值

发布时间:2019-10-28 22:00编辑:编程浏览(174)

      从上面的介绍可以看出,SignalR既然是为实时而生的,这样就决定了其使用场所。具体适用情景有如下几点:

    目录

    C#中字段、属性和构造函数赋值的问题

     

    • 1.1 简介
    • 1.2 创建任务
    • 1.3 使用任务执行基本的操作
    • 1.4 组合任务
    • 1.5 将APM模式转换为任务
    • 1.6 将EAP模式转换为任务
    • 1.7 实现取消选项
    • 1.8 处理任务中的异常
    • 1.9 并行运行任务
    • 1.10 使用TaskScheduler配置任务执行
    • 参考书籍
    • 笔者水平有限,如果错误欢迎各位批评指正!

    提出问题

    首先提出几个问题:

    1、如何实现自己的注入框架?

    2、字段和自动属性的区别是什么?

    3、字段和自动属性声明时的直接赋值和构造函数赋值有什么区别?

    4、为什么只读字段和只读自动属性(只有get没有set访问器)都可以在构造函数中进行赋值?

    5、反射可以给只读字段或者只读属性进行赋值吗?

    6、自动属性和普通属性的区别?

    这些问题是我在试着写自己的注入实现时遇到的问题。这些问题应该在学习C#时的第一节课就应该学到了,我看网上还有人分享说他在面试时遇到面试官问为什么只读字段和只读自动属性可以在构造函数中进行赋值,他没有回答上来,然后他写文章探讨这个问题,却没有得出一个明显的答案,实在可惜。网上关于只读属性有些是写ReadOnly特性的,读到这些文章直接跳过吧,老版本的C#现在看也没什么帮助。

    • 聊天室,如在线客服系统,IM系统等
    • 股票价格实时更新
    • 消息的推送服务
    • 游戏中人物位置的实时推送 : 游戏参考

    给出答案

    2、属性比字段多了get/set访问器;字段是在内存中声明的一个内存空间,可以实实在在的存储值;属性像字段一样使用,却可以有自己的代码段,能赋值取值,是因为访问属性就是调用属性的get/set方法对字段进行取值赋值(或者不操作字段);在MSDN上,建议字段作为类的私有变量使用private/protected修饰,属性则往往作为共有属性使用public修饰;字段的读取和操作都是直接操作内存,属性是调用get/set访问器,所以字段比属性快。

    3、准确来说,没有区别。区别仅仅是直接赋值先执行,构造函数赋值后执行。在生成的IL中间语言(C#代码先编译成IL代码,然后才编译成汇编语言)中,字段直接赋值和构造函数赋值是在同一个代码段中(构造函数中)的。

    4、这个问题可以和上面的问题联合起来回答。构造函数作为实例化一个类的入口,是最先访问的。字段的直接赋值其实也是放在构造函数中执行的,所以才说直接赋值和构造函数赋值没有区别。“只读”的限制只是由C#编译器(CLR)维护的,我觉得全名应该叫做“除构造函数外只读”更加准确,这是C#语法的规则记住就行(这是当然,直接赋值其实是放在构造函数中进行赋值的,如果构造函数不能赋值那只读字段没有值和没有声明一样);

    5、这个问题又可以和上面的问题联系起来一起回答。通过反射可以给自读字段赋值但是无法给只读属性进行赋值(不相信的可以试一下)。对只读字段的赋值是因为绕过了C#编译器(CLR)的只读显示,对只读属性赋值的话是还是调用set访问器对字段进行赋值,因为没有set访问器所以允许后会报错。那么问题来了,那为什么只读自动属性没有set访问器还可以在构造函数中赋值呢?其实只读自动属性在构造函数中进行赋值,实质上是对字段进行赋值,和属性的get/set访问器没有关系。

    6、区别是什么?上面一直强调自动属性,是因为自动属性和普通属性不一样,比如只读普通属性(没有set访问器)无法在构造函数中赋值。在没有自动属性之前,普通属性使用步骤是首先声明一个字段如_id,然后声明一个属性Id,在get和set访问器中做一些操作,这些操作大多数是对字段_id的操作,但是有时候和字段没有关系。普通属性可以像字段一样通过“.”的方式调用,但又像方法一样具有代码段(普通属性从来不开辟内存空间)。

    但是C#3.0之后引入了自动属性,声明方式如public int id { get; set; },C#6.0之后又有了public string FirstName { get; set; } = "Jane"。自动属性肯定开辟了内存空间然后才有了自动属性的直接赋值。其实在类中声明自动属性会在编译成IL中间语言中声明一个隐藏字段,然后生成隐藏字段的get/set方法,然后生成get/set访问器。这里可以解释为什么只读普通属性无法在构造函数中赋值(和直接赋值)而只读自动属性可以在构造函数中赋值(和直接赋值),因为不论直接赋值还是在构造函数中赋值,生成的IL代码中的构造函数中,操作的都是隐藏字段,并没有访问属性的set访问器。(注意这里只是说的类中的自动属性,接口中也是可以有自动属性的,但是接口的自动属性并不会生成隐藏字段只是定义get/set访问器)

     

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

    开始解释

    通过C#生成的IL中间语言代码可以知道的更清楚

        public class User
        {
            public int id = 0;
            public int age { get; set; } = 1;
            public User()
            {
                id = 2;
                age = 3;
            }
        }
    

    图片 1图片 2

    可以看到,自动属性会生成一个名称为 '<age>k__BackingField' 的隐藏私有字段+私有字段的get/set方法+属性代码段;

    可以看到IL代码生成了User的构造函数 .ctor,ctor是构造函数(Constructor)。

    不论直接赋值还是构造函数赋值,都是在.ctor中执行的,并且操作的都是字段,自动属性的赋值操作的是隐藏字段。

      public interface IUser
      {
        int id { get; set; }
      }
    

    图片 3

    可以看到,接口中的自动属性并没有生成隐藏字段。

    Asp.net SignalR是微软为实现实时通信的一个类库。一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。


    其他说明

    1、上文中提到“反射可以给只读字段进行赋值但是无法给只读属性进行赋值”。无法给只读属性进行赋值是因为没有set访问器。但是我们已经知道了可以给字段赋值,并且只读属性会生成隐藏字段,那我们是不是可以通过给隐藏字段进行赋值间接达到给自动属性赋值的目的呢?答案是可以的!

    定义User的只读自动属性

        public class User
        {
            public int age { get;  } = 1;
            public User()
            {
                age = 3;
            }
        }
    

    控制台的反射赋值代码:

                var user = new User();
                try { typeof(User).GetProperty("age").SetValue(user, 9); }
                catch{    Console.WriteLine("只读属性赋值失败");}
                typeof(User).GetField("<age>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(user,9);
                Console.WriteLine(user.age);
                Console.Read();
    

    运行

    图片 4

    2、因为隐藏字段是私有的,所以取到隐藏字段需要  BindingFlags.NonPublic

    3、只读自动属性说明不想被访问到那为什么还要给它赋值呢?这个问题……做着玩,项目中我觉得也没有什么用到的机会……

      signalR内部有两类对象:

    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模式等等。

    1. Http持久连接(Persisten Connection)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
    2. Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

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

    运行结果如下图所示。

    图片 5

     

    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 1Task 2均是运行在主线程上,并非线程池线程。

    图片 6

    本文由金沙国际官网发布于编程,转载请注明出处:使用任务并行库,构造函数赋值

    关键词:

上一篇:字符串反转,调用Tencent云API的实例

下一篇:没有了