晋中北京网站建设,公司规划发展计划书,自媒体平台注册账号下载,类似稿定设计的软件有哪些知识须要不断积累、总结和沉淀#xff0c;思考和写做是成长的催化剂web 内容目录
1、线程Thread 一、生命周期 二、后台线程 三、静态方法 1.线程本地存储 2.内存栅栏 四、返回值 2、线程池ThreadPool 一、工做队列 二、工做线程和IO线程 三、和Thread区别 四、定时器
1、线… 知识须要不断积累、总结和沉淀思考和写做是成长的催化剂web 内容目录
1、线程Thread 一、生命周期 二、后台线程 三、静态方法 1.线程本地存储 2.内存栅栏 四、返回值 2、线程池ThreadPool 一、工做队列 二、工做线程和IO线程 三、和Thread区别 四、定时器
1、线程Thread
.NET中线程操做封装为了Thread类可让开发者对线程进行直观操做。Thread提供了实例方法用于管理线程的生命周期和静态方法用于控制线程的一些访问存储等一些外在的属性至关于工做空间环境变量了网络
一、生命周期
线程的生命周期有建立、启动、可能挂起、等待、恢复、异常、而后结束。用Thread类能够容易控制一个线程的全生命周期多线程
Thread类的构造函数重载能够接受ThreadStart无参数和ParameterizedThreadStart有参数的委托而后调用实例的Start()方法启动线程。Thread的构造函数的带有参数的委托参数是一个object类型由于咱们能够传入任何信息app
Thread t1 new Thread(() {Console.WriteLine($新线程 {Thread.CurrentThread.ManagedThreadId.ToString(00)});
});
t1.Start();
Thread t2 new Thread((obj) {Console.WriteLine($新线程 {Thread.CurrentThread.ManagedThreadId.ToString(00)},参数 {obj.ToString()});
});
t2.Start(hello kitty);线程启动后能够调用线程的Suspend()挂起线程线程就会处于休眠状态不继续执行线程内代码调用Resume()唤醒线程还有一个不太建议使用的Abort()经过抛出异常的方式来销毁线程随后线程的状态就会变为AbortRequested框架
经常使用的还有线程的等待在主线程上启用工做线程后有时须要等待工做线程的完成后主线程才继续工做。能够调用实例方法Join()固然咱们能够传入时间参数来代表我主线程最多等你多久异步
二、后台线程
上一章咱们知道Thread默认建立的是前台线程前台线程会阻止系统进程的退出就是启动以后必定要完成任务的后台线程会伴随着进程的退出而退出。经过设置属性IsBackgroundtrue改成后台线程。另外还能够经过设置Priority指定线程的优先级。但这个并不总会如你所想设置了高优先级就必定最早执行。操做系统会优化调度这也是线程不太好控制的缘由之一函数
三、静态方法
上面介绍的都是Tread的实例方法Thread还有一些经常使用静态方法。有时线程设置不当会有些意想不到的的bug性能
1.线程本地存储
AllocateDataSlot和AllocateNamedDataSlot用于给全部线程分配一个数据槽。像下面例子所示若是不在子线程中给数据槽中放入数据是获取不到其余线程往里面放的数据。测试
var slot Thread.AllocateNamedDataSlot(testSlot);
//Thread.FreeNamedDataSlot(testSlot);
Thread.SetData(slot, hello kitty);
Thread t1 new Thread(() {//Thread.SetData(slot, hello kitty);var obj Thread.GetData(slot);Console.WriteLine($子线程{obj});//obj没有值
});
t1.Start();var obj2 Thread.GetData(slot);
Console.WriteLine($主线程{obj2});在声明数据槽的时候.NET提醒咱们若是要更好的性能请使用ThreadStaticAttribute标记字段。什么意思咱们来看下面这个例子
//
// 摘要:
// 在全部线程上分配未命名的数据槽。 为了得到更好的性能请改用以 System.ThreadStaticAttribute 特性标记的字段。
//
// 返回结果:
// 全部线程上已分配的命名数据槽。
public static LocalDataStoreSlot AllocateDataSlot();例子中的若是不在静态字段上标记ThreadStatic输出结果就会一致。ThreadStatic标记指示各线程的静态字段值是否惟一
[ThreadStatic]
static string name string.Empty;
public void Function()
{name kitty;Thread t1 new Thread(() {Console.WriteLine($子线程{name});//输出空});t1.Start();Console.WriteLine($主线程{name});//输出kitty
}还有一个ThreadLocal提供线程数据的本地存储用法和上面同样在每一个线程中声明数据仅供本身使用
ThreadLocalstring local new ThreadLocalstring() { };
local.Value hello kitty;
Thread t new Thread(() {Console.WriteLine($子线程{local.Value});
});
t.Start();
Console.WriteLine($主线程{local.Value});上面的静态方法用于线程的本地存储TLSThread Local Storage,Thread.Sleep方法在开发调试时也是常常用的让线程挂起指定的时间来模拟耗时操做
2.内存栅栏
先说一个常识问题为何咱们发布版本时候要用Release发布Release更小更快作了不少优化但优化对咱们是透明的计算机里透明认为是个黑盒子内部逻辑细节对咱们不开放和生活中透明意味着彻底掌握了解不欺瞒恰好相反通常优化不会影响程序的运行咱们先借用网上的一个例子
bool isStop false;
Thread t new Thread(() {bool isSuccess false;while (!isStop){isSuccess !isStop;}
});
t.Start();
Thread.Sleep(1000);
isStop true;
t.Join();
Console.WriteLine(主线程执行结束);上面例子若是在debug下能正确执行完直到输出“主程序执行结束”然而在release下却一直会等待子线程的完成。这里子线程中isStop一直为false。首先这是一个由多线程共享变量引发的问题因此咱们建议最好的解决办法就是尽可能不共享变量其次可使用Thread.MemoryBarrier和VolatileRead/Write以及其余锁机制牺牲一点性能来换取数据的安全。上面例子测试若是在子线程while中进行Console.writeLine操做奇怪的发现release下也能正常输出了猜想应该是进行了内存数据的更新
release优化会将t线程中的isStop变量的值加载到CPU Cache中而主线程修改了isStop值在内存中因此子线程拿不到更新后的值形成数据不一致。那么解决办法就是取值时从内存中获取。Thread.MemoryBarrier()就可让在此方法以前的内存写入都及时的从CPU Cache中更新到内存中在此以后的内存读取都要从内存中获取而不是CPU Cache。在例子中的while内增长Thread.MemoryBarrier()就能避免数据不一致问题。VolatileRead/Write是对MemoryBarrier的分开解释从处理器读取从处理器写入。
四、返回值
前面声明线程时能够传递参数那么想要有返回值该如何去作呢Thread并无提供返回值的操做后面.NET给出的对Thead的高级封装给出了解决方案直接使用便可。那目前咱们使用thread类就要本身实现下带有返回值的线程操做都是经过委托实现的这里简单介绍一种共享外部变量也是能够不建议
private FuncT ThreadWithReturnT(FuncT func)
{T t default(T);Thread thread new Thread(() {t func.Invoke();});thread.Start();return () {thread.Join();return t;};
}
//调用
Funcint func this.ThreadWithReturnint(()
{Thread.Sleep(2000);return DateTime.Now.Millisecond;
});
int iResult func.Invoke();2、线程池ThreadPool
.NET起初提供Thread线程类功能很丰富API也不少因此使用起来比较困难何况线程还不都是很像理想中运行因此从2.0开始提供了ThreadPool线程池静态类全是静态方法隐藏了诸多Thread的接口让线程使用起来更轻松。线程池可用于执行任务、发送工做项、处理异步 I/O、表明其余线程等待以及处理计时器。
一、工做队列
经常使用ThreadPool线程池静态方法QueueUserWorkItem用于将方法排入线程池队列中执行若是线程池中有闲置线程就会执行QueueUserWorkItem方法的参数能够指定一个回调函数委托而且传入参数像下面这样
ThreadPool.QueueUserWorkItem((obj) {Console.WriteLine($线程池中线程 {Thread.CurrentThread.ManagedThreadId.ToString(00)} 传入 {obj.ToString()});},hello kitty);二、工做线程和IO线程
通常异步任务的执行不涉及到网络文件等IO操做的计算密集型开发者来调用。而IO线程通常用在文件网络上是CLR调用的开发者无需管。工做线程发起文件访问调用由驱动器完成后通知IO线程IO线程则执行异步任务的回调函数 获取和设置最小最大的工做线程和IO线程
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
ThreadPool.SetMaxThreads(16, 16);
ThreadPool.SetMinThreads(8, 8);三、和Thread区别
若是计算机只有8个核同时能够有8个任务运行。如今咱们有10个任务须要运行用Thread就须要建立10个线程用ThreadPool可能只须要利用8个线程就行节约了空间和时间。线程池中的线程默认先启动最小线程数量的线程而后根据须要增减数量。线程池使用起来简单但也有一些限制线程池中的线程都是后台线程不能设置优先级经常使用于耗时较短的任务。线程池中线程也能够阻塞等待利用ManualResetEvent去通知但通常不会使用。
四、定时器
.NET中有不少能够实现定时器的功能在ThreadPool中咱们能够利用RegisterWaitForSingleObject来注册一个指定时间的委托等待。像下面这样将每隔一秒就输出消息
ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), new WaitOrTimerCallback((obj, b)
{Console.WriteLine($obj{obj},tid{Thread.CurrentThread.ManagedThreadId},datetime{DateTime.Now});
}),hello kitty,1000,false);咱们日常见过比较多的仍是timer类timer类在.net内是好几个地方都有的在System.Threading、 System.Timer、System.Windows.Form、System.Web.UI等里面都有Timer后面都是在第一个System.Threading里的Timer扩展
System.Threading.Timer timer new System.Threading.Timer((obj)
{Console.WriteLine($obj{obj},tid{Thread.CurrentThread.ManagedThreadId},datetime{DateTime.Now});
},hello kitty,1000,1000);timer的底层有一个TimerQueue利用ThreadPool.UnsafeQueueUserWorkItem来完成定时功能和上面咱们使用的ThreadPool定时器有一点区别
实际开发中简单定时timer就够用但通常业务场景比较复杂须要定制个性化的定时器好比每个月几号执行每个月第几个星期几几点执行工做日执行等。所以咱们使用Quarz.NET定时框架后面框架整合时会用到用起来也是很简单的
先就啰嗦这两点吧下一篇应该是Task、Parallel以及Async/Await而后总结介绍下C#的线程模式、线程同步锁机制、异常处理线程取消线程安全集合和常见的线程问题
天长水阔见字如面随缘更新拜了个拜~