门户网站百度百科,查询系统网站模板,网站备案号几位数字 沪,wordpress文章链接一、什么是线程 程序执行的最小单元 一次页面的渲染、一次点击事件的触发、一次数据库的访问、一次登录操作都可以看作是一个一个的进程 在一个进程中同时启用多个线程并行操作#xff0c;就叫做多线程 由CPU来自动处理 线程有运行、阻塞、就绪三态
代码示例#xff1a;
cl…一、什么是线程 程序执行的最小单元 一次页面的渲染、一次点击事件的触发、一次数据库的访问、一次登录操作都可以看作是一个一个的进程 在一个进程中同时启用多个线程并行操作就叫做多线程 由CPU来自动处理 线程有运行、阻塞、就绪三态
代码示例
class Program
{static void Main(string[] args){Thread thread new Thread(() {Print1();});thread.Start();for(int i 0; i 1000; i){Console.Write(0);}Console.Read();}static void Print1(){for (int i 0;i 1000; i){Console.Write(1);}}}运行结果为 可以看到在结果中0和1的输出是交织在一起的原因为两个线程交替着被运行不断反复直到结束。 另外一个常用操作为Sleep();让时间暂停使得线程进入静默状态。
二、前台线程、后台线程与线程池托管
代码举例
class Program
{static void Main(string[] args){Thread thread new Thread(PrintHello);thread.Start();Console.WriteLine(退出主程序);}private static void PrintHello(object? obj){while (true){Thread.Sleep(1000);Console.WriteLine(Hello from PrintHello!);}}
}运行会发现即使主线程运行结束了子线程依旧在持续运行持续运行的子线程就称为前台线程。 一般来说只有等待前台线程运行完毕后程序才可以进行关闭。 与之对应的是 后台线程可以通过thread.IsBackground true;//切换为后台线程将前台 线程切换到后台线程这样再次运行会发现当主线程结束后后台线程就会被强制结束。
一般来说前台线程用于需要时间比较长的等待业务比如监听客户端请求而后台线程适用于时较短的业务比如执行客户端发来的请求后台进程不会影响程序的终止。 托管在线程池中的线程全部为后台线程。 所有使用new Thread创建的线程默认均为前台线程。
三、线程池
示例代码
for(int i 0;i 100; i)
{ThreadPool.QueueUserWorkItem((o) {Console.WriteLine($循环次数{i} 线程id {Thread.CurrentThread.ManagedThreadId});});
}可以看到执行结果出现了id重复的状况原因就是线程池会重复使用已经完成的线程极大节约硬件资源。 另外可以看到for循环有100次但是从输出结果来看只执行了十几次原因为线程池创建的线程均为后台线程只要主程序退出线程池的后台线程就会被停止而主程序main执行的时间很短因此线程池内线程没有来得及执行就被停止了。
对于重要的并发量小的线程需要手动创建管理对于并发量大而又不太重要的线程最好托管到线程池中。
四、结束线程与CancellationToken
不管程序有多少个进程进程内部的资源都是被共享的。所以C#对进程的取消代码作了更高层次的抽象把进程的取消过程封装成为了Token的形式也就是CancellationToken取消令牌。不仅可以使用在多线程中还可以用于异步操作。
class Program
{static void Main(string[] args){CancellationTokenSource cts new CancellationTokenSource();Thread thread new Thread(() { PrintHello(cts.Token); });thread.Start();//下载文件Thread.Sleep(5000);//关闭子进程//cts.Cancel();cts.CancelAfter(3000);//在下载完成后3s失效Console.WriteLine(退出主程序);}private static void PrintHello(CancellationToken tokenSource){while (!tokenSource.IsCancellationRequested){Thread.Sleep(1000);Console.WriteLine(Hello from PrintHello!);}}
}五、Join与IsAlive
对于子线程执行时间不确定的情况需要使用Join的方法加入至主程序执行中或者使用IsAlive方法进行判断
class Program
{static void Main(string[] args){Thread thread new Thread(() { PrintHello(); });thread.Start();//方法一//thread.Join();//方法二while(thread.IsAlive){Console.WriteLine(子线程仍在工作);Thread.Sleep(100);}Console.WriteLine(退出主程序);}private static void PrintHello(){int i 0;while (i 10){Thread.Sleep(new Random().Next(100, 1000));Console.WriteLine(Hello from PrintHello!);}}
}六、资源竞争与线程锁lock
使用线程可以并发的在CPU的核心中执行任务最大化CPU的利用率但是并发执行任务也可能产生各种各样的资源竞争问题。
举例 private static void AddText(){File.AppendAllText(D:\test.txt, $开始{Thread.CurrentThread.ManagedThreadId});Thread.Sleep(100);File.AppendAllText((D:\test.txt, $结束{Thread.CurrentThread.ManagedThreadId});}当两个线程同时需要使用同一个文件资源时产生资源竞争导致系统崩溃。 因此必须保证同一时刻只能有一个线程访问资源避免出现资源恶性竞争。 使用线程锁就可以解决
class Program
{static object lockedObj new object();static void Main(string[] args){for(int i 0; i 10; i){var t new Thread(AddText); t.Start();}Console.WriteLine(退出主程序);}private static void AddText(){lock(lockedObj){File.AppendAllText(D:\test.txt, $开始{Thread.CurrentThread.ManagedThreadId});Thread.Sleep(100);File.AppendAllText(D:\test.txt, $结束{Thread.CurrentThread.ManagedThreadId});} }
}七、异步
在之前项目中我们实现的所有操作都是同步进行的然而当有同时10000个请求发生时会使得用户有很长的等待服务器会等待数据库的响应完成后反馈至用户。 而异步操作要实现不要等待数据库继续执行下一个请求当数据返回数据以后再回头继续处理上一个请求。 然而对于更高级别的数量请求仅仅依靠异步也是不够的因此需要 异步服务每个机器多开进程多个机器组合实现 K8s, Kubernetess容器化分布式部署 .NET Core对容器化非常非常友好、支持度极高
八、异步编程Task 我们使用异步处理并行使用多线程处理并发。 异步逻辑是要基于方法没有依赖关系的例如 class Program{static void Main(string[] args){Calculate();Console.Read();}static void Calculate(){//Task-异步Thread-线程Task.Run(() {Calculate1();});Task.Run(() {Calculate2();});Task.Run(() {Calculate3();});}static int Calculate1(){var result 3;Console.WriteLine($Calculate1: {result});Task.Delay(2000);return result;}static int Calculate2(){var result 4;Console.WriteLine($Calculate2: {result});Task.Delay(3000);return result;}static int Calculate3(){var result 5;Console.WriteLine($Calculate3: {result});Task.Delay(1000);return result;}}但如果是具有依赖关系的例如Caculate2()需要Caculate1()的结果Caculate3需要Caculate1()和Caculate2()的结果那么就需要做如下调整
class Program
{static void Main(string[] args){Calculate();Console.Read();}static void Calculate(){//Task-异步Thread-线程var task1 Task.Run(() {return Calculate1();});var awaiter1 task1.GetAwaiter();//获得异步等待对象awaiter1.OnCompleted(() {var result1 awaiter1.GetResult();//获得异步逻辑的最终计算结果var task2 Task.Run(() {return Calculate2(result1);});var awaiter2 task2.GetAwaiter();awaiter2.OnCompleted(() {var result2 awaiter2.GetResult();var result Calculate3(result1, result2);Console.WriteLine(result);});}); }static int Calculate1(){var result 3;Console.WriteLine($Calculate1: {result});Task.Delay(2000);return result;}static int Calculate2(int a){var result a * 2;Console.WriteLine($Calculate2: {result});Task.Delay(3000);return result;}static int Calculate3(int a, int b){var result a b;Console.WriteLine($Calculate3: {result});Task.Delay(1000);return result;}
}九、C#的异步 async/await
可以看到上面的异步操作代码非常复杂繁琐接下来使用async/await化解上面操作 同步方法 指程序调用某个方法需要等待执行完成以后才进行下一步操作 异步方法 指程序调用某个方法的时候不做任何等待在处理完成之前就返回该方法继续执行接下来的操作即函数在执行完成前就可以先返回调用方然后继续执行接下来的逻辑完成任务的函数 举例
public async Taskint DoSomethingAsync()
{//创建一个计算1万毫秒的任务Taskint longRunningTask LongRunningTaskAsync();//使用await执行这个任务int result await longRunningTask;return result;
}
//假装计算1w毫秒输出为1
private async Taskint LongRunningTaskAsync()
{await Task.Delay(10000);//延迟10sreturn 1;
}1.需要使用async关键词 2.返回类型为void、Task、TaskT 和 IAsyncEnumerableT 3.命名规范Async结尾 4.需要有await表达式 5.要有返回值 6.async函数只能被async函数调用
共有三个部分 第一部分异步调用Taskint longRunningTask LongRunningTaskAsync(); 第二部分执行异步int result await longRunningTask; 第三部分异步方法private async Taskint LongRunningTaskAsync(){}
注
[ 在函数声明中async关键字要放到返回类型之前 ][ async函数本身不创建异步操作只有在调用await的时候才会进行异步操作 ]
下面对之前的异步代码进行优化
class Program
{static void Main(string[] args){Calculate();Console.Read();}static async void Calculate(){var result1 await Calculate1Async();var result2 await Calculate2Async(result1);var result await Calculate3Async(result1, result2);Console.WriteLine(result); }static async Taskint Calculate1Async(){var result 3;Console.WriteLine($Calculate1: {result});await Task.Delay(2000);return result;}static async Taskint Calculate2Async(int a){var result a * 2;Console.WriteLine($Calculate2: {result});await Task.Delay(3000);return result;}static async Taskint Calculate3Async(int a, int b){var result a b;Console.WriteLine($Calculate3: {result});await Task.Delay(1000);return result;}
}十、Task VS. Thread
异步不是多线程 异步用来处理并行多线程用于处理并发
class Program
{static void Main(string[] args){TaskTest();ThreadTest();Console.Read();}static void TaskTest(){var sw new Stopwatch();sw.Start();for(int i 0; i 100; i){Task.Factory.StartNew(() { });}sw.Stop();Console.WriteLine($Task {sw.ElapsedMilliseconds});}static void ThreadTest(){var sw new Stopwatch();sw.Start();for (int i 0; i 100; i){new Thread(() { }).Start();}sw.Stop();Console.WriteLine($Thread {sw.ElapsedMilliseconds});}
}执行结果为 可以看到Task的执行速度要远高于Thread 异步并不会创建线程只是通过主线程来执行同时开出一条分路来执行其他任务非同步分别执行。但是在最后会创建一个非常轻量级的Worker Thread用于通知主程序异步结束也称为回调Call Back.