并发编程 - 线程浅试

360影视 2025-01-17 17:58 2

摘要:Thread有一个带有ThreadStart类型参数的构造函数,其中参数ThreadStart是一个无参无返回值委托,因此我们可以创建一个无参无返回值方法传入Thread构造函数中,代码如下:

前面已经对线程有了初步认识,下面我们来尝试使用线程。

01、线程创建

在C#中创建线程主要是通过Thread构造函数实现,下面讲解3种常见的创建方式。

1、通过ThreadStart创建

Thread有一个带有ThreadStart类型参数的构造函数,其中参数ThreadStart是一个无参无返回值委托,因此我们可以创建一个无参无返回值方法传入Thread构造函数中,代码如下:

代码也相当简单,我们在主线程中通过Thread创建了一个新的线程用来运行BusinessProcess方法,同时通过Thread.CurrentThread.ManagedThreadId打印出当前线程Id。

代码执行结果如下,主线程Id和业务线程Id并不相同。

2、通过ParameterizedThreadStart带参创建

Thread还有一个带有ParameterizedThreadStart类型参数的构造函数,其中参数ParameterizedThreadStart是一个有参无返回值委托,其中参数为object类型,因此我们可以创建一个有参无返回值方法传入Thread构造函数中,然后通过Thread.Start方法把参数传递给线程,代码如下:

我们看看代码执行结果:

该方式有个限制,因为ParameterizedThreadStart委托参数为object类型,因此我们的业务方法也必须要用object类型接收参数,然后再根据实际类型进行转换。

3、通过Lambda表达式创建

通过上面可以知道无论ThreadStart还是ParameterizedThreadStart本质上都是一个委托,因此我们可以直接使用Lambda表达式直接构建一个委托。可以看看以下代码:

代码执行结果如下:

因为Lambda表达式可以直接访问外部作用域中的变量,因此线程传参还可以使用Lambda表达式来实现。

但是这也导致了一些问题,比如下面代码执行结果应该是什么?先自己想想看。

看看执行结果:

和你想想的结果一样吗?

这是因为当在Lambda 表达式中使用任何外部局部变量时,编译器会自动生成一个类,并将该变量作为该类的一个属性。因此这些外部变量并不是存储在栈中,而是通过引用存储在堆中,因此此时param参数实际上在内存中是一个类是一个引用类型,所以两个线程中使用的param都指向了堆中的同一个值。

并且使用Lambda表达式引用另一个C#对象的方式有个专有名词叫闭包。感兴趣的可以去了解下闭包概念。

02、线程休眠

可以通过Sleep方法暂停当前线程,使其处于休眠状态,以尽可能少的占用CPU时间。看如下示例代码,通过在Sleep方法前后打印出当前时间对比,来观察暂停线程效果。

代码执行结果如下:

可以发现暂停线程前后正好差了10秒钟。

03、线程等待

线程等待指让程序等待另一个需要长时间计算的线程运行完成后,再继续后面操作。而使用Thread.Sleep方法并不能满足需求,因为当前并不知道执行计算到底需要多少时间,因此可以使用Thread.Join。如上一小节中代码,当代码执行到Thread.Join方法时,则线程会处于阻塞状态,只有线程执行完成后才会继续往下执行。具体示例可以看上一小节。

04、线程其他方法

此外线程还有暂停、恢复、中断、终止等线程方法,这里就不介绍了,因为一些方法已经弃用没有必要再花经历学习了。

05、异常处理

对于线程中的异常需要特别注意,对于一个Thread子线程所产生的异常,默认情况下主线程并不能捕捉到,可以查看下面示例:

运行结果如下:

可以看到在主线程中并没有捕捉到子线程抛出的异常,而导致程序直接中断。因此我们在处理线程异常时需要特别注意,可以直接在线程中处理异常。

06、何时应该使用线程

线程有很多优点,但也并不是万能的,因为每一个线程都会产生大量的资源消耗,包括:占用大量内存空间,线程的创建、销毁和管理,线程之间的上下文切换,以及垃圾回收的消耗。

举个简单例子,比如一个小餐馆,有一个厨师,一个下单员,客户下单给下单员,下单员把客户下的菜单传递给厨师。假如现在客户很多一个下单员忙不过来,老板决定再添加一个下单员,此时下单的效率可以提升一倍,但是厨师还是一个,那么就会导致当厨师和A下单员交接的时候,B下单员只能等着,并且因为之前厨师和A下单员长时间合作形成了彼此默契,这是再和B下单员交接的时候效率可能并不高,因此最终整体效率并不一定提升多少。如果把厨师比作CPU处理器,下单员比作线程,如果要想餐馆的整体效率提升那么在增加下单员的时候,必须要相应的添加厨师,才能使得餐馆最大效率的提升。

因此并不是说无脑的添加线程就可以使得程序效率提升,需要按需使用。

比如在以下使用场景可以考虑使用多线程:文件多写、网络请求、数据库查询、图像处理、数据分析、定时任务等。


来源:IT规划师

相关推荐