IE盒子

搜索
查看: 152|回复: 1

.Net 多线程与锁全在这里

[复制链接]

6

主题

9

帖子

22

积分

新手上路

Rank: 1

积分
22
发表于 2023-1-19 10:49:22 | 显示全部楼层 |阅读模式
一台服务器能运行多少个线程,大致取决于CPU的管理能力。CPU负责线程的创建、协调、切换、销毁、暂停、唤醒、运行等。
一个应用程序中,必须有一个进程,一个进程可同时多个线程协作处理。
同步:单线程,每一步都执行结束并返回结果,下一步处于等待,阻塞程序流
异步:多线程,不需要等待执行结束,可继续执行下一步,形成并行处理,无序的不可预测的执行顺序
前台线程:主线程退出后,子线程直至完成计算。
后台线程:主线程退出后,子线程也会停止退出。
线程的应用

常用的线程应用方式


  • new Thread
  • ThreadPool.QueueUserWorkItem 后台线程
  • delegate 委托 invoke
  • Task.Run / Task.Factory.StartNewd
  • Parallel
  • await / async
ThreadPool 线程池

线程池线程是后台线程。每个线程均使用默认的堆栈大小,以默认的优先级运行,并且位于多线程单元中。一旦线程池中的线程完成任务,它将返回到等待线程队列中。这时开始即可重用它。通过这种重复使用,应用程序可以避免产生为每个任务创建新线程的开销。
每个进程只有一个线程池。由线程池统一管理每个线程的创建/分配/销毁。
// 设置可同时并行运行的线程数ThreadPool.SetMinThreadsThreadPool.SetMaxThreads// 用线程池中的后台线程执行方法ThreadPool.QueueUserWorkItem(方法1);Task

Task所用到的线程同样来自于ThreadPool。
Action<object> action = (object obj) =>{    Console.WriteLine("Task={0}, obj={1}, Thread={2}",    Task.CurrentId, obj,    Thread.CurrentThread.ManagedThreadId);};// Create a task but do not start it.Task t1 = new Task(action, "alpha");// Construct a started taskTask t2 = Task.Factory.StartNew(action, "beta");// Block the main thread to demonstrate that t2 is executingt2.Wait();// Launch t1 t1.Start();Console.WriteLine("t1 has been launched. (Main Thread={0})",Thread.CurrentThread.ManagedThreadId);// Wait for the task to finish.t1.Wait();// Construct a started task using Task.Run.String taskData = "delta";Task t3 = Task.Run( () => {    Console.WriteLine("Task={0}, obj={1}, Thread={2}",    Task.CurrentId, taskData,    Thread.CurrentThread.ManagedThreadId);});// Wait for the task to finish.t3.Wait();// Construct an unstarted taskTask t4 = new Task(action, "gamma");// Run it synchronouslyt4.RunSynchronously();// Although the task was run synchronously, it is a good practice// to wait for it in the event exceptions were thrown by the task.t4.Wait();// Task 的返回值(等待返回结果)Task<int> task = new Task<int>(() =>{    //...    return 0;});task.Start();int result = task.Result;// 仅仅启动一个TaskTask.Run(() =>{    //...});// 批量启动 Task// 同时并行运行至少30个线程的方式ThreadPool.SetMinThreads(30, 50);ThreadPool.SetMaxThreads(50, 70);for (int i = 0; i < 30; i++){    Task.Run(() =>    {        //...    });}// 取消线程运行,更多取消方式参考 https://learn.microsoft.com/zh-cn/dotnet/standard/threading/cancellation-in-managed-threadsCancellationTokenSource cts = new CancellationTokenSource();Task.Run(() =>{    if (Console.ReadKey(true).KeyChar.ToString().ToUpperInvariant() == "C")    {        // 取消        cts.Cancel();    }});Parallel

多线程并行处理的 Parallel.Invoke,Parallel.For,Parallel.ForEach;无法确保顺序处理。
// 示例:并行运行几个方法Parallel.Invoke(方法1, 方法2, () => 方法3, () => 方法4)// ParallelOptions 参数设定// 先指定启用30个线程同时处理的设置ParallelOptions paroptions = new ParallelOptions();paroptions.MaxDegreeOfParallelism = 30;// 指定数量的线程,并行执行200遍List<int> datas = new List<int>();Parallel.For(0, 200, paroptions, index =>{    datas.add(index);});// 指定数量的线程,并行读取数据Parallel.ForEach(datas, paroptions, (item) =>{    Console.WriteLine(item);});await / async

创建后台进程并行处理,并取得处理结果。
// 执行 async 方法(后台线程执行)Task<string> _task_result_1 = t1.Func1();// 主线程不等返回结果,继续往下执行// 执行方法string _result_2 = t1.Func2();// 以上两个方法并行执行// 取 Func1 的运行结果string _result_1 = await _task_result_1;// 整合两个方法的运行结果int _total_ = _result_1.length + _result_2.length;常用的线程锁

lock

通用的标准锁,封装自应用级锁Monitor类,需要有线程共享的锁标识,告知其它线程是否等待。
Monitor

程序级锁,于单个应用范围内,通过获取或释放用于标识资源的对象上的 lock 来授予对共享资源的相互独占访问权限。
// 给要操作的对象加把锁,排他锁Monitor.Enter(T);// 释放当前锁,允许其它线程使用了Monitor.Exit(T);SpinLock

快速的、低级别的简易锁。不同于标准锁,适合应用于简单的、非耗时的逻辑处理,此场景下更多时候性能优于标准锁。
如果应用场景并非足够简单或存在不确定性的可能,SpinLock 将比标准锁开销更大。
static SpinLock _spinlock = new SpinLock();bool lockTaken = false;try{    // 加锁    _spinlock.Enter(ref lockTaken);    // ...}finally{    // 释放    if (lockTaken) _spinlock.Exit(false);}Interlocked

更细化的锁,针对性的场景时用来代替lock,包括非空、递增等针对场景的应用,性能优于lock。
// 递增场景案例// lock 方式lock(lockObject){      myField++;  }// Interlocked 的替代方式System.Threading.Interlocked.Increment(myField);Semaphore

进程间同步,跨应用限制可同时访问某一资源或资源池的线程数,允许同时共享的线程数
// 创建时,定义同时的默认线程数和最多线程数(起始线程数,最多线程数)Semaphore sem = new Semaphore(10, 20);// 上锁sem.WaitOne();// 释放锁(一次释放线程数)sem.Release(3);Mutex

进程间同步,系统级锁,所以可跨进程跨应用。
// 阻挡锁住当前块,其它线程处于等待// 超时后返回false,不允许其它线程进入Mutex.WaitOne(timeout);// 释放当前,一个,允许其它线程进入Mutex.ReleaseMutex();线程安全

多个线程可以调用单个对象的属性和方法时,对这些调用进行同步处理是非常重要的。否则,一个线程可能会中断另一个线程正在执行的任务,可能使该对象处于无效状态。其成员不受这类中断影响的类叫做线程安全类。
在早期的.NET版本中,提供了常用的线程安全类,通过 Synchronized 属性和 lock(xxx.SyncRoot) 实现线程同步。
如:ArrayList,Hashtable// Synchronized 与 SyncRoot 协作,实现线程安全// 线程安全创建对象,并实现多线程同步Hashtable ht = Hashtable.Synchronized(new Hashtable());lock (ht.SyncRoot){    ht.Add("key1", true);}线程安全的高性能集合类

.NET Framework 4 引入了几种专为支持多线程添加和删除操作而设计的集合类型。为了实现线程安全,这些类型使用多种高效的锁定和免锁定同步机制。同步会增加操作的开销。开销数取决于所用的同步类型、执行的操作类型和其他因素,例如尝试并行访问该集合的线程数。
在System.Collections.Concurrent命名空间,其中包含多个线程安全且可缩放的集合类。多个线程可以安全高效地从这些集合添加或删除项,而无需在用户代码中进行其他同步。编写新代码时,只要将多个线程同时写入到集合时,就使用并发集合类。
直接在其它线程中对集合操作,不需要手动加锁:
ConcurrentBag  无序元素集合的线程安全实现
ConcurrentStack  LIFO(后进先出)堆栈的线程安全实现
ConcurrentQueue  无序元素集合的线程安全实现
ConcurrentDictionary  键值对字典的线程安全实现

出处:
https://www.cnblogs.com/Sol-wang/archive/2022/10/20/14793008.html

版权声明:本文来源于网友收集或网友提供,仅供学习交流之用,如果有侵权,请转告版主或者留言,本公众号立即删除。
<hr/>



支持小薇

腾讯云福利

【腾讯云】多款云产品1折起,买云服务器送免费机器,最长免费续3个月
https://cloud.tencent.com/act/cps/redirect?redirect=2446&cps_key=a960b71e694dcc2f8b5a943535abd4a2&from=console
关注公众号:DotNet开发跳槽觉得不错,请点个在看呀
回复

使用道具 举报

3

主题

8

帖子

17

积分

新手上路

Rank: 1

积分
17
发表于 2025-6-20 23:16:22 | 显示全部楼层
占坑编辑ing
回复

使用道具 举报

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

本版积分规则

快速回复 返回顶部 返回列表