一、线程

​ 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

有了进程为什么要有线程

​ 进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在三点上:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

  • 进程间的数据无法直接共享

线程的出现原因

正常情况下,我们在启动一个程序的时候。这个程序会先启动一个进程,启动之后这个进程会拉起来一个线程。这个线程再去处理事务。也就是说真正干活的是线程,进程这玩意只负责向系统要内存,要资源但是进程自己是不干活的。默认情况下只有一个进程只会拉起来一个线程。

​ 多线程顾名思义,就是同样在一个进程的情况同时拉起来多个线程。上面说了,真正干活的是线程。进程与线程的关系就像是工厂和工人的关系。那么现在工厂还是一个,但是干活的工人多了。那么效率自然就提高了。因为只有一个进程,所以多线程在提高效率的同时,并没有向系统伸手要更多的内存资源。因此使用起来性价比还是很高的。但是多线程虽然不更多的消耗内存,但是每个线程却需要CPU的的参与。

​ 相当于工厂虽然厂房就一间,可以有很多的工人干活。但是这些工人怎么干活还得靠厂长来指挥。工人太多了,厂长忙不过来安排一样效率不高。所以工人(线程)的数量最好还是在厂长(cpu)的能力(内核数)范围之内比较好。

线程出现的背景

​ 60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)出现,可以满足多个运行单位,而多个进程并行开销过大。

  因此在80年代,出现了能独立运行的基本单位——线程(Threads)

  注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.

     每一个进程中至少有一个线程。 

线程特点

  1)轻型实体

  线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。

  线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述,

  2)独立调度和分派的基本单位。  在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。 

  3)共享进程资源。  线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。

  4)可并发执行。  在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。

线程和进程的关系

  • 根据上图可以看出,进程包含线程。也就是说默认情况下 一个进程肯定会有一个线程的,这个线程叫主线程。

  • 多线程也就是在一个进程里面开出多个线程。多进程里面也可以包含多线程。

  • 多进程之间是不可以直接通讯的。但是由于多线程是被同一个进程包裹,故多线程中资源共享即可以直接通讯。

  • 进程本身不能够执行

  • 进程和线程不能比较谁快谁慢,两个没有可比性,进程是资源的集合,线程是真正执行任务的,进程要执行任务也要通过线程

  • 启动一个线程比启动一个进程快,线程上下文切换比进程上下文切换要快得多

python线程模块的选择

 Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。  一般避免使用thread模块,使用更高级别的threading模块,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突。

二、threading模块

multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性

开启线程的两种方式

1、方式一

  from threading import Thread
import time

def func(n):
time.sleep(2)
print("线程是%s"%n)
global g
g = 0
print(g)

if __name__ == '__main__':
g = 100
t_l = []
for i in range(10):
t = Thread(target=func,args=(i,))
t.start()
t_l.append(t) for t in t_l:
t.join()
print("主线程G",g)

  

 

2、方式二

import threading
import time def func(n):
time.sleep(2)
print("线程是%s" % n)
print('子线程的ID号A', threading.current_thread().ident)
global g
g = 0
print('子线程中的g', g) class Mythread(threading.Thread): def __init__(self, arg, *args, **kwargs):
super().__init__(*args, **kwargs)
self.arg = arg def start(self):
print('start-----')
super().start() # 调用父类的start()和run()方法 def run(self):
print("类中的子线程", self.arg)
super().run()
print('子线程的ID号B',threading.current_thread().ident) if __name__ == '__main__':
g = 100
t1 = Mythread('hello', target=func, name="MyThread", args=('nick',))
# 第一个参数是用在Mythread类中的,后面的3个参数用在创建的func子线程中,args必须是可迭代的
# 这里的func也可以直接写在Mythread中的run()里,这时这里的run()不用再继承父类的run()
t1.start()
# t1.run()
t1.join()
print('主线程中的g', g)
print('主线程的ID号---', threading.current_thread().ident)

  简化版:

import time
from threading import Thread
class MyTread(Thread):
def __init__(self,arg):
super().__init__()
self.arg = arg
def run(self):
time.sleep(1)
print(self.arg)
print('直接在这里写子进程的代码') t = MyTread('hello')
t.start()

  

  

多线程与多进程区别

1、运行方式不同:

进程不能单独执行,它只是资源的集合。

进程要操作CPU,必须要先创建一个线程。

所有在同一个进程里的线程,是同享同一块进程所占的内存空间。

2、关系

进程中第一个线程是主线程,主线程可以创建其他线程;其他线程也可以创建线程;线程之间是平等的。

进程有父进程和子进程,独立的内存空间,唯一的标识符:pid。

3、速度

启动线程比启动进程快。

运行线程和运行进程速度上是一样的,没有可比性。

线程共享内存空间,进程的内存是独立的。

4、创建

父进程生成子进程,相当于复制一份内存空间,进程之间不能直接访问

创建新线程很简单,创建新进程需要对父进程进行一次复制。

一个线程可以控制和操作同级线程里的其他线程,但是进程只能操作子进程。

5、交互

同一个进程里的线程之间可以直接访问。

两个进程想通信必须通过一个中间代理来实现。

测试下进程和线程谁开启的速度快

  from multiprocessing import Process
from threading import Thread
import time

def func(n):
n + 1


if __name__ == "__main__":
start_t = time.time()
t_li = []
for i in range(100):
t = Thread(target=func, args=(i,))
t.start()
t_li.append(t)
for t in t_li: t.join()
druing_time1 = time.time() - start_t

start_p = time.time()
p_li = []
for i in range(100):
p = Process(target=func, args=(i,))
p.start()
p_li.append(p)
for p in p_li: p.join()
druing_time2 = time.time() - start_t
print(druing_time1,druing_time2)

  

分析:根据执行结果显示:开启线程的速度比进程的速度快上百倍以上。

看下多线程和多进程的PID

  
  from threading import Thread
from multiprocessing import Process
import os

def work():
print('函数内hello',os.getpid())

if __name__ == '__main__':
#part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid',os.getpid())

#part2:开多个进程,每个进程都有不同的pid
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid',os.getpid())

  

测试下多线程内共享数据的情况

  
  from  threading import Thread
from multiprocessing import Process
import os
def work():
global n
n=0

if __name__ == '__main__':

# 多进程的情况
# n=100
# p=Process(target=work)
# p.start()
# p.join()
# print('主',n)
#毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100
#
#

# 多线程的情况
n=1
t=Thread(target=work)
t.start()
t.join()
print('主',n)
#查看结果为0,因为同一进程内的线程之间共享进程内的数据,
# 这表示线程内数据是共享的

  

多线程下的socket客户端和服务端

服务端

  
  import socket
from threading import Thread


server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8081))
server.listen(5)
buffer_size = 1024

def chat(conn,addr):

res = conn.recv(buffer_size)
print("addr",addr,res.decode("utf-8"))
msg = input("请输入:").strip()
conn.send(msg.encode("utf-8"))

def func():

while True:
print("服务器开始运行了")
conn,addr = server.accept()
res = Thread(target=chat,args=(conn,addr)).start()

func()

  

客户端

  
  import socket

buffer_size = 1024
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8081))

while True:
msg = input("请输入").strip()
client.send(msg.encode("utf-8"))
res = client.recv(buffer_size)
print(res.decode("utf-8"))

  

分析:这里可测试同时开启多个客户端与服务端聊一次天

参考链接

[1]https://blog.csdn.net/u013421629/article/details/79208227

[2]https://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

Python之路(第四十一篇)线程概念、线程背景、线程特点、threading模块、开启线程的方式的更多相关文章

  1. Python使用Threading模块创建线程

    使用Threading模块创建线程,直接从threading.Thread继承,然后重写__init__方法和run方法: #!/usr/bin/python # -*- coding: UTF-8 ...

  2. 使用threading模块创建线程

    #_author:来童星#date:2019/12/17#使用threading模块创建线程import threading,timedef process(): for i in range(3): ...

  3. Python之网路编程利用threading模块开线程

    一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1 1.创建线程的开销比创建进程的开销 ...

  4. Python——threading模块(线程)

    一.threading模块的对象 Thread:表示一个执行线程的对象 Lock:锁 Rlock:可重入锁对象 Condition:条件变量对象,使得一个线程等待另一个线程满足特定的“条件” Even ...

  5. 利用threading模块开线程

    一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1.创建线程的开销比创建进程的开销小, ...

  6. threading模块创建线程

    什么是线程 (thread) 线程也是一种多任务编程方式,可以使用计算机的多核资源.线程被称为轻量级的进程. 线程特征 *线程计算机多核分配的最小单位 *一个进程可以包含多个线程 *线程也是一个运行的 ...

  7. python之路基础篇

    基础篇 1.Python基础之初识python 2.Python数据类型之字符串 3.Python数据类型之列表 4.Python数据类型之元祖 5.Python数据类型之字典 6.Python Se ...

  8. python之路——基础篇(2)模块

    模块:os.sys.time.logging.json/pickle.hashlib.random.re 模块分为三种: 自定义模块 第三方模块 内置模块 自定义模块 1.定义模块 将一系列功能函数或 ...

  9. python之路第一篇

    一.python环境的搭建 1.window下环境的搭建 (1).在 https://www.python.org/downloads/ 下载自己系统所需要的python版本 (2).安装python ...

随机推荐

  1. Eye sketch - ES

      An interesting painting program, the interface is a blank drawing board, touch the bottom of the r ...

  2. [教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等

    [教程]Ubuntu16.04安装QQ,Tim,微信,百度网盘等 本文参考这篇blog step 1 先安装 deep-win环境. 戳这里下载压缩包 解压后在文件夹里打开终端,输入 sudo sh ...

  3. 操作excel文件爬取nvd.nist数据

    #!/usr/bin/env python # encoding: utf-8 #@author: jack import random from time import sleep import p ...

  4. luoguP2173 [ZJOI2012]网络 LCT

    链接 luogu 思路 颜色很少,开10个lct分别维护 if (Hash.count(make_pair(u, v)) && Hash[make_pair(u, v)] == col ...

  5. 【数位DP】【P4127】[AHOI2009]同类分布

    Description 给出两个数 \(a,~b\) 求出 \([a~,b]\) 中各位数字之和能整除原数的数的个数. Limitations \(1 \leq a,~b \leq 10^{18}\) ...

  6. IPython 安装

    从 python 第三方库列表下载安装包 https://www.lfd.uci.edu/~gohlke/pythonlibs/ 然后使用 pip 进行安装 软件会自动安装到 python 安装路径的 ...

  7. LOAM笔记

    CSDN有篇结合paper分析代码的博文,下面是我对paper的理解: 1. 综述 整个LOAM本质就是一个激光里程计,没有闭环检测,也就没有图优化框架在里面,该算法把SLAM问题分为两个算法同时运行 ...

  8. 本地VS调试服务器 IIS 程序

    由于读书的关系,毕业后选择在武汉,工作三年,至今年5月份挪窝到沿海某二线城市,换城市相当于裸辞,一切从头开始,新的城市,新的居住地,新的空气,新工作,新挑战.一直忙忙碌碌,孜孜不倦的汲取着,担心脱队, ...

  9. PB级数据实时查询,滴滴Elasticsearch多集群架构实践

    PB级数据实时查询,滴滴Elasticsearch多集群架构实践  mp.weixin.qq.com 点击上方"IT牧场",选择"设为星标"技术干货每日送达 点 ...

  10. visual studio code 执行 sp1 文件

    需要安装插件PowerShell 如提示 Unsupported PowerShell version 2.0, language features are disabled  (不支持的PowerS ...