多线程和锁和原子操作和内存栅栏(一)

线程的定义是执行流的最小单元,而进程是一个逻辑线程容器,用来隔离线程。

Task类封装了线程池线程,启动的所有线都由线程池管理,他提供了很多使用方便的API函数,使多线程开发变得容易。


上述代码中我启动了一个线程,并在线程方法中使用了异步关键字,异步方法实现了一个状态机,能够在等待任务完成时继续执行之后的代码,这里利用该特性实现线程顺序执行,

并且上述代码使用了ContinueWith方法,这是一个任务执行完成时的延续方法,参数x代表了完成的任务,Delay方法是延迟方法,

状态机其实是编译器生成的一个结构体,内部使用了goto 关键字,把异步方法封装在状态机中,由状态机去执行异步方法,await前后的线程上下文相同,在GUI程序中可以把耗时的方法通过 await + Task 的方式让其他线程来执行,而且并不会阻塞UI线程。await前后的上下文是相同的,但如果不想相同可以使用ConfigureAwait(false),如果在GUI程序中,这会导致异常,因为界面的元素只能由UI线程修改,同时这也可以避免死锁。


有时候在客户端我们的代码需要被C++函数回掉时或者任何我们都不能改变别人使用多线程来执行我们只能用UI线程才能执行的代码时,我们需要强制同步UI线程上下文使得代码正常运行,不是this.Dispacher方法而是使用TaskScheduler类来提前获取UI线程上下文,并开启任务,让任务在指定上下文中运行,


如果希望使用一个任务又希望能够顺序、同步执行,可以使用GetAwaiter().GetResult()组合调用,或者使用Wait()方法,但更建议使用前面一种


如果任务拥有返回值可以直接使用Result属性


并不建议使用Wait()  Result,有时候这会造成死锁。


内存栅栏:变量在进入内存栅栏并读取时,变量的值已经写入完毕,离开栅栏时,变量的值被读取时也已经写入完毕,只有一个线程能够进入内存栅栏,这保证了多线程的数据同步。


在上面这个例子中,我启动了三个线程。并对变量target递增,结果理应是30000,而输出结果却不是,这是因为在多线程场景下,可能会在某一个时刻几个线程同时读取到一个值然后递增,并重新赋值,导致变量数据被同样的值覆盖。


而使用了内存栅栏则能很好的解决问题,内存栅栏保证在同一时刻,只有一个线程可以进入,


当然使用volatile关键字也是一样的。


原子操作是一种一次性的操作,不会出现中间变化,并且是线程安全、线程同步的。通过原子操作同样可以实现之前的例子。



待续。。。



展开阅读全文

没有更多推荐了,返回首页