工具下载地址:链接: https://pan.baidu.com/s/1mLxHzpE91IFA0AaUELGCpw 提取码: 2vn3
一、Windbg的使用
运行Windbg-->file->Attach to a Process 选择一个进程
.loadby sos clr 首先需要加载sos和clr
!threads 显示线程信息
!teb 显示TEB信息
!dumpdomain 显示程序域
!clrstack 查看当前的调用堆栈(首先需要点击线程的osid)
点击线程的State可以查看线程的状态
!help 帮助命令
!FinalizeQueue 查看终结器
!threadpool 查询线程池
!dumpheap -stat 查看clr的托管堆中的各个类型的占用情况
二、线程的开销
1,空间上的开销
①线程内核数据结构,其中有osid,线程上下文(包含CPU寄存器集合的内存块)
②TEB(线程环境块)
③用户模式堆栈(默认分配1M空间)。会放参数、局部变量..
④内核模式堆栈(系统底层级别的堆栈空间)
2,时间上的开销
①进程启动时会加载很多的dll(托管和非托管)。当线程开启或销毁时,会标记所有非托管dll
②时间片切换。当开启的线程多余系统线程数时,会发生时间片切换(时间片切换休眠时间为30ms)
三、线程的生命周期
Start:线程开启
Suspend:线程暂停
Resume:恢复暂停的线程
Intterupt:中断线程(会抛出异常)
Abort:销毁线程(会抛出异常)
Join:等待线程执行完毕
四、工作线程和IO线程
工作线程:给一般的异步任务执行。其中不涉及到网络、文件这些IO 【开发者调用】
IO线程:一般用在文件、网络IO上【CLR调用】
五、Task
Task=Thread(控制能力)+ThreadPool (在此基础上封装)
Thread:容易造成时间+空间的浪费。控制能力可以
ThreadPool :性能好,控制能力弱(控制权在CLR)。比如控制Thread超时、阻塞、取消等
1,Task的三种启动方式
Task底层都是由不同的TaskScheduler支撑(TaskScheduler相当于Task的CPU处理器)
默认TaskScheduler是ThreadPoolTaskScheduler
六、任务调度器
任务都需要经过TaskScheduler
1,ThreadPoolTaskScheduler(task默认调用此TaskScheduler)
这个方式底层如果标记为LongRunning则使用Thread。否则调用ThreadPool
2,SynchronizationContextTaskScheduler(同步上下文TaskScheduler)
案例:
private void button1_Click(object sender, EventArgs e) { new TaskFactory().StartNew(() => { //耗时的操作 Thread.Sleep(3000); }).ContinueWith(t => { label1.Text = "执行完成"; }, TaskScheduler.FromCurrentSynchronizationContext()); }
七、多线程模型
1,同步编程模型(SPM)
2,异步编程模型(APM)
xxxbegin
xxxend
委托给线程池执行的。FileStream(BeginRead,EndRead)配对方法、Action委托,都可以异步执行
3,基于事件的编程模型(EAP)
xxxAsync这样的事件模型。WebClient
4,基于Task的编程模型(TAP)
微软大力推广。APM和EAP都能包装成Task使用
static void Main(string[] args) { Console.WriteLine("主线程。线程id:{0}", Thread.CurrentThread.ManagedThreadId); //异步编程模型(APM)包装成Task var action = new Action(() => { Console.WriteLine("执行Action。线程id:{0}", Thread.CurrentThread.ManagedThreadId); }); var task = Task.Factory.FromAsync(action.BeginInvoke, action.EndInvoke, string.Empty); //基于事件的编程模型(EAP)包装成Task TaskCompletionSource<int> source = new TaskCompletionSource<int>(); WebClient web = new WebClient(); web.DownloadDataCompleted += (obj, e) => { try { source.TrySetResult(e.Result.Length); } catch (Exception ex) { source.TrySetException(ex); } }; web.DownloadDataAsync(new Uri("https://www.oyunkeji.com")); Console.WriteLine(source.Task.Result); Console.ReadKey(); }
八、锁机制
1,用户模式锁
就是通过一些cpu指令或者一个死循环达到thread等待和休眠
①易变结构 volatile
阻止cpu缓存,实时从内存中读取变量的值
private static volatile bool isStop= false;
②互锁结构 Interlocked
③旋转锁 SpinLock
public static SpinLock spinLock = new SpinLock(); static int nums = 0; static void Run() { for (int i = 0; i < 10; i++) { try { bool b = false; spinLock.Enter(ref b); Console.WriteLine(nums++); } catch (Exception ex) { spinLock.Exit(); } } } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Console.ReadKey(); }
2,内核模式锁
调用win32底层代码,来实现thread的各种操作
万不得已的情况下,不要使用内核模式锁,代价太大
①自动事件锁
场景:使用火车票进闸机,一人一人进
namespace ConsoleApp1 { class Program { private static AutoResetEvent areLock = new AutoResetEvent(true); static int nums = 0; static void Run() { for (int i = 0; i < 10; i++) { areLock.WaitOne(); Console.WriteLine(nums++); areLock.Set(); } } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Console.ReadKey(); } }
②手动事件锁
场景:栅栏阻挡行人、车辆等。适合批量
class Program { private static ManualResetEvent mrLock = new ManualResetEvent(false); static int nums = 0; static void Run() { for (int i = 0; i < 10; i++) { mrLock.WaitOne();//等待5秒后才会执行。拦一批值 Console.WriteLine(nums++); } } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Thread.Sleep(5000); mrLock.Set(); Console.ReadKey(); } }
③信号量
场景:传入允许线程的数量
class Program { private static Semaphore sLock = new Semaphore(1, 1); static int nums = 0; static void Run() { for (int i = 0; i < 10; i++) { sLock.WaitOne(); Console.WriteLine(nums++); sLock.Release(); } } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Thread.Sleep(5000); Console.ReadKey(); } }
④互斥锁
class Program { private static Mutex mLock = new Mutex(); static int nums = 0; static void Run() { for (int i = 0; i < 10; i++) { mLock.WaitOne(); Console.WriteLine(nums++); mLock.ReleaseMutex(); } } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Thread.Sleep(5000); Console.ReadKey(); } }
这四种锁都有一个WaitOne方法,因为他们都继承WaitHandle,这四种锁本是同根生,底层都是通过SafeWaitHandle来对win32api的一个引用
⑤读写锁
如果写入线程时间太久,比如10s。这个时候读的线程会被卡死。从而超时
class Program { private static ReaderWriterLock rwLock = new ReaderWriterLock(); static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Factory.StartNew(() => { Read(); }); } Task.Factory.StartNew(() => { Write(); }); Console.ReadKey(); } static void Read() { while (true) { rwLock.AcquireReaderLock(int.MaxValue); Thread.Sleep(100); Console.WriteLine("read {0}", Thread.CurrentThread.ManagedThreadId); rwLock.ReleaseReaderLock(); } } static void Write() { while (true) { Thread.Sleep(3000); rwLock.AcquireWriterLock(int.MaxValue); Thread.Sleep(3000); Console.WriteLine("write {0}", Thread.CurrentThread.ManagedThreadId); rwLock.ReleaseWriterLock(); } } }
⑥监视锁
class Program { private static object objLock = new object(); static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Console.ReadKey(); } static int nums = 0; private static void Run() { for (int i = 0; i < 100; i++) { var b = false; try { Monitor.Enter(objLock, ref b); Console.WriteLine(nums++); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { if (b) Monitor.Exit(objLock); } } } }
3,混合锁
用户模式+内核模式(场景是最多的)
在用户模式下内旋,如果超过一定的阔值,会切换到内核锁。在内旋的情况下,我们会看到大量的Sleep(0)、Sleep(1)、Yield等
①ManualResetEventSlim
class Program { private static ManualResetEventSlim mrsLock = new ManualResetEventSlim(); static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Thread.Sleep(1000); mrsLock.Set(); Console.ReadKey(); } static int nums = 0; private static void Run() { for (int i = 0; i < 1000; i++) { mrsLock.Wait();//等待1秒后才会执行。拦一批值 Console.WriteLine(nums++); } } }
②SemaphoreSlim
class Program { private static SemaphoreSlim mrsLock = new SemaphoreSlim(1,10); static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Run(() => { Run(); }); } Console.ReadKey(); } static int nums = 0; private static void Run() { for (int i = 0; i < 1000; i++) { mrsLock.Wait(); Console.WriteLine(nums++); mrsLock.Release(); } } }
③ReaderWriterLockSlim
class Program { private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); static void Main(string[] args) { for (int i = 0; i < 5; i++) { Task.Factory.StartNew(() => { Read(); }); } Task.Factory.StartNew(() => { Write(); }); Console.ReadKey(); } static void Read() { while (true) { rwLock.EnterReadLock(); Thread.Sleep(100); Console.WriteLine("read {0}", Thread.CurrentThread.ManagedThreadId); rwLock.ExitReadLock(); } } static void Write() { while (true) { Thread.Sleep(3000); rwLock.EnterWriteLock(); Thread.Sleep(3000); Console.WriteLine("write {0}", Thread.CurrentThread.ManagedThreadId); rwLock.ExitWriteLock(); } } }