C# 基础知识系列- 12 任务和多线程

[复制链接]

下载APP可以快速和圈友联系

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
0. 前言

按例一份前言,在先容使命和多线程之前,先先容一下异步和同步的概念。我们之间先容的常识点都是在同步履行,所谓的同步就是一行代码一行代码的履行,就像是我们平常乘坐地铁经过安检通道一样,设想我们每小我都是一行代码,我们依次经过安检仪器的时辰就是同步。

那末,什么是异步呢?有一个时候操纵率的故事,讲的是在烧水的同时,顺便预备茶叶,清洗茶杯等工序可以节省时候。这个故事就是异步的一个典型典范。异步浅显的迁就是不停息也不期待当前耗时的流程履行完成,继续履行后续的流程。

那末这和使命与多线程有什么关系呢?在C#中,基于使命可以很简单的建立一个异步法式大概异步方式;同时使命也是一个简单的多线程形式。不外值得留意的是,C#的异步可以由多线程实现,但多线程更多的是用来实现并行。所谓并行,望文生义,就是多使命同时履行,这里的使命指的是法式需要完成的事,而不是C#中的使命机制。

这一篇是《C#根本常识系列》的一篇,简单先容一下若何建立、利用使命和多线程,这部分的内容很多,包括有很多留意事项,将会另开一个系列专门讲授C#的异步和并行编程,名字暂定为《C#异步编程系列》。

C# 根本常识系列- 12 使命和多线程-1.jpg

1. 线程

领会过计较机的人能够晓得法式最小履行单元是线程,最小资本分派单元是进程。进程里必定最少有一个线程,而一个法式也必定最少有一个进程。这里不外多的先容进程和线程的区分于关系,只需要记着线程是法式最小履行单元,我们在开辟中最常用的也是线程。

在很多不太松散的编程教程中,城市把多线程和并行化作等号。可是这里有一个很奥妙的区分,对于单核CPU来说,多进程和多线程一样,都不会发生并行的结果;对于多核CPU而言,多进程必定是并行的,可是多线程则纷歧定并行。所以C#中,线程更多的用作异步处置上,而不是并行计较上。

在C#法式中,需要援用System.Threading。C#的入门级线程操纵只需要晓得Thread类、一个带参数的无返回值方式和一个不带参数的无返回值方式,这三个要点便可以了。
1.1 建立一个线程

var thread = new Thread(() =>
{
});
以上示例代码演示了假如建立一个线程。但建立了线程,并不代表线程就会运转。

说到这里就必须说一下线程的状态,一般情况线程分为五个阶段,也就是五种状态:别离是预备、停当、运转、阻塞、灭亡。固然在分歧的地方,状态能够会细分为更多的级别,这里只做初步的先容。状态之间的切换以下:

C# 根本常识系列- 12 使命和多线程-2.jpg

线程的状态之间切换顺序有着严酷的限制,而且只能从停当态由CPU切换到运转态,运转态没法从其他状态切换曩昔,而且这一步的切换开辟者不能控制。

现在,我们回到线程的建立方式,先来看看Thread机关方式的声明:
public Thread (System.Threading.ParameterizedThreadStart start);
public Thread (System.Threading.ThreadStart start);
碰到了两个没见过的范例,我们继续看看?
public delegate void ParameterizedThreadStart(object obj);
public delegate void ThreadStart();
到这里,线程的建立为我们揭开了它的答案。按照之前《C# 根本常识系列- 11 拜托和事务》那篇的先容,我们可以很明白的获得 ThreadStart是一个 无返回值也没有参数的拜托,而ParameterizedThreadStart暗示有一个object的参数。所以,建立线程的时辰,可以间接传一个方式进去。

有的同学能够要问了,为什么建立线程的拜托参数那末少?这里触及到一个并发概念,由于线程拜候过量的主线程能够会致使锁,所以最好的线程理论就是让线程的运转连结一个相对封锁的情况。

固然,C#的线程实在放宽了这部分的限制,这部分将在《C#异步编程系列》中继续探讨。

现在我们回过甚来,再看看若何建立一个标准的线程:
class Program
{
    static void Main(string[] args)
    {
        var thread1 = new Thread(ThreadTest1);
        var thread2 = new Thread(ThreadTest2);
    }

    ///
    /// 不带参数的线程
    ///
    public static void ThreadTest1()
    {
        // 营业代码
    }
    ///
    /// 带参数的线程
    ///
    ///
    public static void ThreadTest2(object obj)
    {
        //营业代码
    }
}
其中thread1就是一个没有参数的线程,thread2是一个带参数的线程。

注:Main方式是C#法式进口的牢固写法,之前一切的示例代码都是在这个方式里履行的,后续这部分会在《C#根本篇之开辟工具和项目标根基结构》这一篇中具体先容,这里先记着这是一个牢固写法。
1.2 启动并利用线程


在启动线程之前,我们先先容一个概念:主线程。主线程指陪伴着当前法式启动而启动的线程,以代码来看就是Main方式地点线程。

线程经过挪用Thread.Start方式,来将线程标志为停当态。

留意:线程不能间接进入运转态,该状态只能由CPU决议。

所以上一小节的建立的两个线程可以经过以下方式告诉已经预备就续:
thread1.Start();
咦?能否是少了一个?留意力集合的小伙伴会发现,我没有演示thread2的挪用方式。thread2与threa1有个分歧的地方,thread2的拜托参数有一个参数。那末必定Start也有一个对应的带参版本的重载,所以thread2就会有以下两种挪用方式:
thread2.Start();

object obj;// 省略来历
thread2.Start(obj);
两种方式有什么区分吗?

有,可是区分不大。第一种挪用方式对于方式ThreadTest2而言就是参数为null,第二种就是参数为obj的值。所以第一种挪用约即是thread2.Start(null)。
1.3 停息或烧毁线程


这一小节的题目是,停息或烧毁线程。当线程运转起来后,假如没有突发情况大概外力干涉会间接运转到竣事。这时辰,后续法式感觉这个线程履行时候太长,需要停息大概取消线程的履行,那末就需方法会一下若何停息大概烧毁线程了。
thread1.Suspend();//挂起
thread1.Resume();//继续
中断线程,也就是停止线程:
thread1.Abort();// 已挂起的线程没法中断
强迫停止烧毁:
thread1.Interrupt();//在履行中的线程没法停止
以上是线程操纵的根基概念,这部分并不是为了能让大师精通多线程,这是为了让大师有个初步概念。在C# 中,建立一个线程需要传递一个拜托进去,由于拜托的性质,并没有限制能否是静态方式,所以这里也可以传一个工具的方式。固然了,我们非常不提倡这样做,由于会致使一些多线程范畴里的一些题目。
2. 使命

C#中的使命与线程的区分不是很大,由于C#的使命就是基于线程实现的,而使命比线程更友爱,利用也更方便,固然利用也加倍复杂。不外对于开辟者而言,使命取消了线程的状态切换,只保存了有限的一部分。而且,在C# 更保举利用使命,使命也是对线程的进一步笼统和改良。
2.1 建立一个使命


如线程不异的一点是,使命的建立也是经过传递一个方式(严酷上讲是一个拜托)。分歧的是,线程的拜托没有返回值而且也不接管从线程返回的值,而使命则分歧,挪用方可以期待使命是有返回值的而且也可以一般利用。

我们先来看看使命是什么,使命的命名空间System.Threading.Tasks,使命的类有以下两种声明:
public class Task : IAsyncResult, IDisposable;
public class Task : System.Threading.Tasks.Task;
第一个,没有泛型的Task类暗示一个没有返回值的使命;

第二个,泛型Task类暗示该使命有一个返回值,返回值的范例为传递进来的泛型参数。

两个使命类的初始化类似于Thread类,不外与之分歧的是 泛型Task的参数是Func,都有一个带Object参数的拜托。

与线程分歧,使命的建立就有很多种方式:

1 经过机关函数建立
var task1 = new Task(() => { });
var task2 = new Task(()=>
{
    int i = 0;
    return i;
});
2 利用使命工场:
var task1 = Task.Factory.StartNew(() => { });
var task2 = Task.Factory.StartNew(() =>
{
    int i = 0;
    return i;
});
3 经过Task.Run建立:
var task1 = Task.Run(() => { });
var task2 = Task.Run(() =>
{
    int i = 0;
    return i;
});
以上三种方式建立的使命是等效的。固然现实上使命的建立并非只要这么几种,但这几种是使命建立的根本,利用频次相当高。
2.2 履利用命


与线程分歧的是,使命建立完成以后就会自动履行,不需要挪用方式。

关于使命的运转有以下需要留意的地方:

使命的运转不会阻塞主线程;

主线程竣事后,使命一定也会竣事;

使命可以IsCompleted属性肯定使命能否履行完成,所以可以经过拜候使命工具的IsCompleted确认该使命能否履行完成,但有一个题目,这个属性只会暗示当前使命能否完成。所以假如需要期待使命完成,则可以经过拜候Wait()方式,强迫主线程期待使命竣事。

假如利用的使命是泛型Task也就是待返回值的使命,可以经过拜候Result属性获得使命履行成果。成心机的地方就是,这个属性能获得到成果的时辰,也是使命履行完成的时辰,所以不需要挪用Wait()或IsCompleted来判定使命能否完成。

注:经过机关方式建立的使命需要挪用 Start方式才能启动,而经过Task.Run和Task.Factory.StartNew建立的则不需要。
3. 总结

C#中使命基于线程,对其做了更多的笼统和封装,将线程的粒度进一步细分。所以线程在C#中就没有那末重要了,使命逐步替换了线程在C#法式中的职位。

使命与线程,有共通的地方,也有完全纷歧样的地方。线程的运转情况相对封锁,所以线程出现毛病致使线程中断,不会影响主线程的运转。但使命则纷歧样了,使命与主线程的关联性更大,一旦使命出现异常致使使命中断,假如没有正确处置,则会影响主线程的运转。

以上是本篇的全数内容,也请大师期待一下《C#异步编程系列》吧。
温馨提示:
好向圈www.kuaixunai.com是一个专业经验分享交流平台,你可以在这里发布专业经验,也可以发布需求与服务,禁止带推广链接、联系方式、违法词等,违规将封禁账号。 下载好向圈客户端可以随时随地交流经验,也可以和圈友发起聊天成为好友哦!
回复

使用道具 举报

已有(1)人评论

跳转到指定楼层
骗你是小小小狗 发表于 2020-4-24 19:54:35
简单介绍下任务和多线程的使用,大家快来看看有啥问题没
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本圈子积分规则