C#多线程编程序--聊聊线程
这篇文章主要给您讲解几个基本问题,什么是线程?为什么要用线程?线程万能?这几个问题。我这篇博客是在该系列另外几篇博客之后写的,之所以不往下继续写,而是回到最初的问题,是因为我感觉到我没有很好的讲解开头的部分,没有很好的介绍线程的基础知识,因此有了写这篇博客的想法。本文不会一五一十的介绍线程,那是百度百科和维基百科的事,我的目的是和您坐下来聊一聊,以我自己粗浅的理解,给您简单讲解一下线程的相关内容。读完,您会对线程有个基本的了解,然后你再去看本系列的其他部分,就会简单些,不会一头雾水。
什么是线程?为什么要用线程?
在一个pc中有很多应用,每一个应用都被分配了一块内存,这些内存就是进程。当你在桌面点击不同的应用时,cpu会在这些应用中切换,以保证尽快的响应你的操作。这些进程能够独立运行,彼此互不影响,一个进程出了问题不会影响其他的进程。很多程序为了提升用户的操作体验,会有好几个进程,要开启一个新进程要消耗很多的时间(大概几秒),有没有一个“轻量级”的进程,能让cpu执行它,且不需要那么多的内存?这就是线程。线程相对进程要简单的多,分配一个新线程很快。现在的cpu执行都是以线程为单位来调度的,而不是进程,并且多核处理器可以并行执行多个线程。
线程万能?
线程怎么看都比进程要好很多,那么线程有什么缺点?相对于进程,线程的调度有很多的优势。这就导致有人对线程过分高估,干点什么事都新开个线程,期待它会提高用户体验。殊不知线程的创建和切换也会消耗资源。一个线程栈大概1m,切换线程需要cpu从一个线程上下文保存起来,以备以后再次调用该线程。然后读取想要切换的线程的上下文,如果新建线程,还要分配线程的内存。当频繁创建和切换线程很频繁,或者切换线程后执行的人无很短,这些消耗就必须要考虑了。
线程的消耗主要集中在创建和线程切换。非常快的任务是不适合调派其他线程来执行的。而是那些需要等待的任务才适合交给其他线程来执行。如链接其他服务,文件读写等IO操作,再者就是长时间的计算,这些任务如果都在主线程执行,会造成“卡顿”,这样的卡顿是让人沮丧的,如果卡顿的时间过长,我们会以为我们的电脑死机了。此时如果将这样的任务交给另一个线程去执行,而主线程能够继续响应用户其他的操作,这就能提升用户体验,线程的消耗才是“物超所值”。另一个消耗是创建线程的消耗。当创建一个新线程,并执行结束后,这个线程就再也不会唤醒。可以把这些死线程利用我来,当真正需要新线程的时候再创建新线程。做法是把线程统一管理,重复利用,这就是线程池。
线程的两大消耗已经有了解释,线程还有一个问题就是数据共享的问题。我们说过,线程很轻量级,为了使线程够轻,采用的办法是多个线程共享一个进程的内存,这就会导致多个线程执行时,数据会产生不同步的问题。这个线程已经修改了某一个数据,另一个线程并没有读取到该数据,还是按老值来操作。
线程同步的解决办法有两个,一是原子操作,另一个是锁。原子操作是一组API,该API能够保证该操作的“原子性”,所谓原子性,就是其他线程对同样资源的访问都会在之前或者之后发生,比如对某一数据的读操作,如果该操作是原子的,那么就是说所有对该资源的写操作都是在该读操作的之前或者之后发生,而不会发生正在读该数据时,其他线程完成了对该资源的写操作,对该数据的读总是读到最新的。这样的操作的优点很明显,简单,且不阻塞线程,不会造成死锁。其缺点是能够实现的功能有限。
另一个线程同步的方法是锁,锁的原理是在不想被并发执行的代码的周围加上一个锁,类似这样:
var m_lock = new SomeLock();
m_lock.Enter();
//some code
m_lock.Leave();
当有线程执行到m_lock.Enter()时,其内部是一个变量(比如bool型),会将该变量置为true,当其他线程走到m_lock.Enter时,看到变量为true,要么不断尝试,要么挂起,等待锁被释放后,被唤醒。当执行完some code后,m_lock.Leave()执行,会将变量置为false,这样其他线程就可以访问该段代码。锁的种类大致分为三种,1是自旋锁,当执行到m_lock.Enter()时,发现被锁定,会不断尝试获得锁,像这样:
//比如这个i就是那个bool型变量
//注意,本例是简单讲解,实际的锁,此处应调用原子操作,如Interlocked中的方法,或者将变量i设为volatile。
while(i == true){
//一些黑科技
}
线程会不断的while,一直循环,直到i==false,表示锁被释放了,他才继续执行。这种锁的优点是单线程时执行非常块,但是在等待锁释放时,会不停的自旋,以求最快的进入锁,这会白白的浪费CPU。
2 内核锁,内核锁也是一个变量,只不过这个变量不用你手动的去改变,而是由系统内核来对其进行维护。这种锁的优点是当其他线程尝试进入锁,而该锁已被锁定时,会阻塞该线程,然后内核负责在锁释放后,唤醒该线程,这会节约CPU资源,在锁被释放之前,线程一直挂起,用来执行其他操作。但是其缺点就是当单线程时,由于内核锁是内核构造,因此用户线程需要切换到内核中,读取该变量,然后再切换回用户线程,最后才能得到返回值,这一系列的操作造成了资源的消耗,所以很慢。如果该资源的大部分时间都是单线程,并且通常锁定的时间都很短,那么用自旋锁就比内核锁更合适,相反就用内核锁。
3 混合锁,混合锁结合了12两种锁的优点,单线程时很快,多线程时阻塞,还有多线程时先自旋一小段时间,如果被锁定的资源很快被释放,那么久不需要调用内核锁,否则调用内核锁。我们经常会见到 lock(objec)语句,其内部是调用Monitor.Enter和Monitor.Leave,这个 Monitor就是个混合锁。
上面讲了这几种锁,各有优缺点。在多线程编程中,基本原则是尽量将代码设计的合理些,减少锁的使用,若必须使用锁,则根据自身的情况,选择合理的锁。
以上,我介绍了线程的基本知识和线程安全的相关知识,若您对其中的内容有任何不懂得地方,欢迎在评论区与我互动。
C#多线程编程序--聊聊线程的更多相关文章
- [转]c++多线程编程之pthread线程深入理解
多线程编程之pthread线程深入理解 Pthread是 POSIX threads 的简称,是POSIX的线程标准. 前几篇博客已经能给你初步的多线程概念.在进一 ...
- 多线程编程之pthread线程深入理解
不同的平台和操作系统上 进程和线程的实现机制不完全一致 但是一般来说线程栈都是独立的 只要得到地址就可以相互访问 Pthread是 POSIX threads 的简称,是POSIX的线程 ...
- posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序
posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属 ...
- 线程模型、pthread 系列函数 和 简单多线程服务器端程序
一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 “线程实现”建立在“进程控制”机制之上,由用 ...
- C#多线程之旅(3)——线程池
v博客前言 先交代下背景,写<C#多线程之旅>这个系列文章主要是因为以下几个原因:1.多线程在C/S和B/S架构中用得是非常多的;2.而且多线程的使用是非常复杂的,如果没有用好,容易造成很 ...
- QT核心编程之Qt线程 (c)
QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...
- [转] iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首 ...
- iOS 多线程编程之Grand Central Dispatch(GCD)
介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其它的对称多处理系统的系统.这建立在任务并行运行的线程池模式的基础上的. 它 ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
随机推荐
- Fiddler使用简单介绍
一,fiddler简介 1.1,什么是fiddler Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的"进出&qu ...
- CSS布局(五) 网页布局方式
网页实质是块与块之间的位置,块挨着块,块嵌套块,块叠着块. 三种关系:相邻,嵌套,重叠. 下面介绍网页布局的常用几种方式 1.一列布局: 一般都是固定的宽高,设置margin : 0 auto来水平居 ...
- mac 上格式化磁盘出现MediaKit报告设备上的空间不足以执行请求的解决办法
1.问题描述: 我使用是一个2T移动硬盘,分了5个区 2.分析原因:因为mac OSX的日志式格式需要有EFI分区进行引导,而我的移动硬盘是没有EFI分区的,这样的话就会出现问题: 3.解决办法: 1 ...
- crontab定时任务(centos)
cron服务是Linux的内置服务,但它不会开机自动启动.可以用以下命令启动和停止服务: /sbin/service crond start /sbin/service crond stop /sbi ...
- ansible基本使用教程
转载请注明出处http://www.cnblogs.com/chenxianpao/p/7360349.html 一. 介绍 1. 简介 ansible是新出现的自动化运维工具,基于Pytho ...
- CodeForces-748B
关键在于判断是否能够得到解决办法,我的思路就是用一个数组来记录每个小写字母对应的按键,如果它出现对应两个级以上不同的按键那么就说明不能得出解决办法,直接打印'-1'.如果能够得出解决办法,就扫描一下数 ...
- UVA-10954 贪心+优先队列
贪心策略:每次选取最小的两个数相加,将和作为新的数加入序列,再从里面取两个最小的数...直到只剩下一个数.优先队列正好对付这题. PS :以前在学校OJ做过几乎一毛一样的这题 AC代码: #inclu ...
- Spring 框架系列之 JDBC 整合实例
微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.Spring框架整合 DAO 模板 JDBC:org.springframework.jdb ...
- MongoDB的DBREF 使用.
首先要记一下根据 DBREF 的ObjectId 以及根据 ref 集合为条件查询问题. 在不同的可视化客户端里面显示的问题. //某客户端显示这样,直接CMD查询也是这样显示.这样我无法看懂find ...
- Hbase配置java客户端
1.修改windows配置文件 C:\WINDOWS\system32\drivers\etc\hosts 将远程hbase和zookeeper主机的IP地址加进去 54.0.88.53 H ...