C#多线程
C#多线程是C#学习中必不可少的知识,在实际开发中也能有效的提升用户体验,和程序性能。
文章目录
- C#多线程
- 前言
- 一、什么是线程、什么是进程、什么是协程?
- 协程
- 优点
- 缺点
- 线程
- 优点
- 缺点:
- 进程
- 优点
- 缺点:
- 二、C# 中线程的使用
- 委托创建线程
- Thread类创建线程
- 线程池创建线程
- 任务创建线程
- 总结
前言
随着业务的不断发展,程序的数据处理量需求也越来越高,例如,电商项目中的库存同步,和商品信息拉取等,一个门店都是几千个品种,每个品种都有几十甚至上百的的批次,如果是个连锁有一万家门店,那么这种情况库存同步如果用单线程处理的话效率是极其低的,同步几天都同步不完,接下来就该利用多线程来优化了。
一、什么是线程、什么是进程、什么是协程?
协程
协程,英文名是 Coroutine, 又称为微线程,是一种用户态的轻量级线程。协程不像线程和进程那样,需要进行系统内核上的上下文切换,协程的上下文切换是由程序员决定的。
优点
- 无需系统内核的上下文切换,减小开销;
- 无需原子操作锁定及同步的开销,不用担心资源共享的问题;
- 单线程即可实现高并发,单核 CPU 即便支持上万的协程都不是问题,所以很适合用于高并发处理,尤其是在应用在网络爬虫中。
缺点
- 无法使用 CPU 的多核
协程的本质是个单线程,它不能同时用上单个 CPU 的多个核,协程需要和进程配合才能运行在多 CPU上。当然我们日常所编写的绝大部分应用都没有这个必要,就比如网络爬虫来说,限制爬虫的速度还有其他的因素,比如网站并发量、网速等问题都会是爬虫速度限制的因素。除非做一些密集型应用,这个时候才可能会用到多进程和协程。
- 处处都要使用非阻塞代码
写协程就意味着你要一直写一些非阻塞的代码,使用各种异步版本的库,比如后面的异步爬虫教程中用的 aiohttp 就是一个异步版本的request库等。 不过这些缺点并不能影响到使用协程的优势。
线程
线程,英文名是Thread,线程是进程中的一个实体,作为系统调度和分派的基本单位。Linux下的线程看作轻量级进程。
它是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
优点
- 创建一个新线程的代价比创建一个新进程要小的多;
- 与进程相比,线程之间的切换需要操作系统做的工作要少很多;
- 线程占用的资源要比进程少很多;
- 能充分利用多处理器的可并行数量;
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务;
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现;
- I/O密集型应用,为了能提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
缺点:
- 性能损失
一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器,如果计算密集型线程的数量比可用的处理器多,那么可能会有较大性能损失,性能损失只增加了额外的同步和调度开销,而可用资源不变。
- 健壮性降低
编写多线程需要更全面深入的考虑,在一个多线程程序里面,因时间分配上的细微偏差或者因为共享了不该共享的变量造成的影响很大,线程是缺乏保护的;
- 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些OD函数会对整个进程造成影响;
- 编程难度提高
调试多线程程序比单线程程序困难的多。
进程
进程,英文名是:process,当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。
优点
-
顺序程序的特点:具有封闭性和可再现性;
-
程序的并发执行和资源共享。多道程序设计出现后,实现了程序的并发执行和资源共享,提高了系统的效率和系统的资源利用率。
缺点:
-
操作系统调度切换多个线程要比切换调度进程在速度上快的多。而且进程间内存无法共享,通讯也比较麻烦。
-
在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
它们之间的关系如下:
进程可以包含多个线程,线程里又可以包含多个协程
二、C# 中线程的使用
在C#中创建线程的方式:
- 利用委托创建线程
- 利用Thread类创建线程
- 利用线程池创建线程
- 利用任务创建线程
委托创建线程
.net Core | .net Framework |
---|---|
不支持 | 支持 |
.net Framework 示例代码:
Func<int, int, int> a = (d, b) => d + b;
a.BeginInvoke(3, 4, CallBack, a);
Console.ReadKey();
static void CallBack(IAsyncResult ar) //系统会自动将该参数填充
{Func<int, int, int> b = ar.AsyncState as Func<int, int, int>;int res = b.EndInvoke(ar);Console.WriteLine("最后得到的结果是:" + res);
}
虽然.net Core 不可以但是这里还是给大家展示
微软官网文档
以下是.net Core6.0实现委托实现AOP的方法
using System.Reflection;DelegateExtension extension = new DelegateExtension();
Console.WriteLine(extension.Show());
Console.WriteLine("主线程执行...");
Console.Read();public abstract class BaseAttribute : Attribute
{public abstract Func<int> Do(Func<int> action);}
public class BeforAttribute : BaseAttribute
{public override Func<int> Do(Func<int> action){Console.WriteLine("执行之前基础操作!");Func<int> acc = new Func<int>(action.Invoke);return acc;}
}public class CoreFunc
{[Befor]public int Methond(){Console.WriteLine("主要执行的方法");return 1;}}
public class DelegateExtension
{public int Show(){CoreFunc ca = new CoreFunc();//反射对象Type type = ca.GetType();MethodInfo methodInfo = type.GetMethod("Methond");Func<int> action = () => { return Convert.ToInt32(methodInfo.Invoke(ca, null)); };//判断这个方法上面是否有BeforAttribute 特性,没有就直接执行方法 if (methodInfo.IsDefined(typeof(BeforAttribute), true)){foreach (BaseAttribute ba in methodInfo.GetCustomAttributes(typeof(BaseAttribute), true)){action = ba.Do(action);}}return action.Invoke();}}
执行结果如下:
Thread类创建线程
.net Core | .net Framework |
---|---|
支持 | 支持 |
以下是代码示例
string name = "子线程传入的参数";
Thread t = new Thread(() => Console.WriteLine(name));
t.Start();
Console.WriteLine("主线程执行");
Console.ReadLine();
执行结果如下:
线程池创建线程
.net Core | .net Framework |
---|---|
支持 | 支持 |
void ThreadMethod(object name) { Console.WriteLine("子线程线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString("00")); }
ThreadPool.QueueUserWorkItem(ThreadMethod);
Console.WriteLine("主线程执行");Console.ReadLine();
执行结果如下:
任务创建线程
.net Core | .net Framework |
---|---|
支持 | 支持 |
//第一种方法
Task task = new Task(() => { Console.WriteLine("第一种方法输出!"); });
task.Start();
//第二种方法
TaskFactory taskF = new TaskFactory();
taskF.StartNew(() => Console.WriteLine("第二种方法输出!"));//连续型任务(按顺序执行)
Task task = new Task(() => Console.WriteLine("你好"));
task.Start();
Task task2 = task.ContinueWith((a) => Console.WriteLine("hello"));Console.ReadLine();
执行结果:
参考自”猫不在“博主博客
总结
以上就是线程的一些基础知识,线程涉及的知识还是比较广的,比如线程资源争夺,线程锁,线程权重等知识,后面我会为大家分享并拿实际案例来讲解。