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线程(一 ...
随机推荐
- Python自动化--语言基础3--字典、函数、全局/局部变量
字典 dict1 = {'name':'han','age':18,'class':'first'} print(dict1.keys()) #打印所有的key值 print(dict1.values ...
- Java经典编程题50道之三十九
写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度. public class Example39 { public static void main(String[] a ...
- 移动端页面点击a标签会有半透明的阴影或红色边框的bug
好久没有更新了,今天来一发 ^_^ 最近在写移动端页面,测试时发现一个a标签的bug:无论是iOS端还是Android端都存在,当点击a标签,会有一个矩形的透明的阴影闪一下(不同的浏览器阴影的颜色还不 ...
- mac下使用mysql控制台命令行
命令行中输入 open .bash_profile 然后将 alias mysql=/usr/local/mysql/bin/mysqlalias mysqladmin=/usr/local/mysq ...
- java4 - 函数(方法)
一.学习大纲: 1. 定义函数可以将功能封装 2. 函数的级别都是同级别的,不能进行函数套用 3. 便于对该功能进行复用 4. 函数只有被调用才能被执行 5. 函数的出现提高了代码的复用性 6. 函数 ...
- 简单了解下OSI七层模型的作用以及常见网络协议含义和所用端口
OSI七层模型及每层的作用 包括:物理层.数据链路层.网络层.传输层.会话层.表示层.应用层物理层用来建立.维护.断开物理连接数据链路层建立逻辑链接.进行硬件地址寻址.差错校验等功能网络层进行逻辑地址 ...
- uva1025 动态规划
这道题的边界是dp(T,N)=0,状态dp(i,j)表示在时间i.第j个车站最少等待时间,有三个决策:1.等1分钟 2.如果有向左的车,向左 3.若果有向右的车,向右 转移方程就是dp(i,j)=m ...
- UVA1602
实现的细节很多,学到了如何翻转.旋转.平移,get很多技巧,值得一做. AC代码: #include<cstdio> #include<cstring> #include< ...
- day2(字符串、格式化输出、运算符、流程控制)
一.字符串 在Python中,加了引号的字符都被认为是字符串! 单引号.双引号.多引号的区别? 单引号和 双引号没有任何区别,但是某种情况下需要单双配合 如 msg = " My name ...
- android/底层获取上下文对象
public class ContextUtils { private static Context applicationContext = null; public static Context ...