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

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

一是由于进程是资源的拥有者,创建和撤销和切换存在较大的时空开销,因此需要引入轻型进程,

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

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

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

线程与进程的区别以下区别4点

1. 地址空间和其他资源;进程间互相独立,同一进程的各线程间共享,某进程内的线程在其他进程不可见.

2 .通信,进程间通信IPC ,线程间可以直接读写进程数据段来进行通信。

3.调度和切换,线程上下文切换比进程上下文切换要快.

4. 在多线程操作系统中,进程不是一个可执行的实体.

在多线程操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为cpu的基本单位,是花费最小开销的实体

线程具有以下属性:

1. 轻型实体 。

线程的实体包括程序,数据和tcb ,线程是动态概念,他的动态特性由线程控制块TCB 描述。

2. 独立调度和分派的基本单位.

3. 共享进程资源.(不需要进行IPC间通信了)

线程在同一进程中的各个线程,都可以共享该进程所有的资源,这表现在所有线程都具有相同的进程ID,这意味着线程可以访问该进程的每一个内存资源.由于同一个进程内的线程共享内存和文件,所以线程之间相互通信不必调用内核.

4 .可并发执行

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

不同的进程之间是充满敌意的,彼此是抢占、竞争cpu的关系,如果迅雷会和QQ抢资源。而同一个进程是由一个程序员的程序创建,所以同一进程内的线程是合作关系,一个线程可以访问另外一个线程的内存地址,大家都是共享的。

1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程

2. 在同一个进程中,如果一个线程关闭了文件,而另外一个线程正准备往该文件内写内容呢?

因此,在多线程的代码中,需要更多的心思来设计程序的逻辑、保护程序的数据。

全局解释器锁GIL 

全局解释器锁-GIL 同一时刻只能有一个线程访问CPU.锁的是线程.

python解释器是python语言的问题吗 ? 

是cpython解释器的特性. 这是python的一个弊病. 也是所有解释型语言的弊端.

 Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

  在多线程环境中,Python 虚拟机按以下方式执行:

  a、设置 GIL;

  b、切换到一个线程去运行;

  c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));

  d、把线程设置为睡眠状态;

  e、解锁 GIL;

  d、再次重复以上所有步骤。
  在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

  thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。

线程的创建 Threading.Thread类

一 、创建线程的方式一

from threading import Thread
import time
def sayhi(name):
time.sleep()
print('%s say hello' %name) if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.start()
print('主线程')

输出结果:

二、创建线程的方式二

from threading import Thread
import time
class sayhi(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
time.sleep()
print('%s say hello' %self.name) if __name__ =='__main__':
t =sayhi('egon')
t.start()
print('主线程')

输出结果

 多线程与多进程 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())
print()
#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():
print('hello') if __name__ == '__main__':
#在主进程下开启线程
t=Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
''' #在主进程下开启子进程
t=Process(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
主线程/主进程
hello
'''

内存数据的共享问题

import os
import time
from threading import Thread
def func(a,b):
global g
g =
print(g,os.getpid()) g =
t_lst = []
for i in range():
t = Thread(target=func,args=(i,))
t.start()
t_lst.append(t)
for t in t_lst : t.join()
print(g)

输出结果

0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0 14444
0

进程是最小的内存分配单位

线程是操作系统调度的最小单位

线程被cpu执行了

进程内至少含有一个线程

进程中可以开启多个线程.

开启一个线程锁需要的时间远远小于开启一个进程。

多个线程内部有自己的数据栈,数据不共享。

全局变量在多个线程之间是共享的.

线程守护进程

1 .守护进程随着主进程代码执行的结束而结束.

2.守护线程会在主线程结束之后等待其他子线程结束而结束.

import time
from threading import Thread
def func1():
while True:
print('*'*)
time.sleep()
def func2():
print('in func2')
time.sleep() t = Thread(target=func1,)
t.daemon = True #守护线程在主线程结束之后会随着其他子线程的结束而结束。
t.start()
t2 = Thread(target=func2,)
t2.start()
t2.join()
print('主线程')

输出结果:

**********
in func2
主线程
**********
**********
**********
**********
**********

Join方法 :主线程一直等待全部的子线程结束之后,主线程自身才结束,程序退出。

join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止,

import time
from threading import Thread
def func1():
while True:
print('*'*)
time.sleep()
def func2():
print('in func2')
time.sleep() t = Thread(target=func1,)
t.daemon = True
t.start()
t2 = Thread(target=func2,)
t2.start()
t2.join()
print('主线程')

输出结果

D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
**********
in func2
**********
**********
**********
**********
**********
主线程

  

线程Lock (互斥锁) 只有一把钥匙

线程的数据是共享的所以,一般我们会上锁,而 进程一般不会上锁锁,因为数据本身是隔离的。

# noodle_lock  = Lock()
# fork_lock = Lock()
# def eat1(name):
# noodle_lock.acquire()
# print('%s拿到面条啦'%name)
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# print('%s吃面'%name)
# fork_lock.release()
# noodle_lock.release()
#
# def eat2(name):
# fork_lock.acquire()
# print('%s拿到叉子了'%name)
# time.sleep()
# noodle_lock.acquire()
# print('%s拿到面条啦'%name)
# print('%s吃面'%name)
# noodle_lock.release()
# fork_lock.release()
#
# Thread(target=eat1,args=('alex',)).start()
# Thread(target=eat2,args=('Egon',)).start()
# Thread(target=eat1,args=('bossjin',)).start()
# Thread(target=eat2,args=('nezha',)).start()

输出结果 

D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
alex拿到面条啦
alex拿到叉子了
alex吃面
Egon拿到叉子了
bossjin拿到面条啦

递归锁 RLOCK (递归锁.)解决死锁问题. 

from threading import RLock   # 递归锁
fork_lock = noodle_lock = RLock() # 一个钥匙串上的两把钥匙
def eat1(name):
noodle_lock.acquire() # 一把钥匙
print('%s拿到面条啦'%name)
fork_lock.acquire()
print('%s拿到叉子了'%name)
print('%s吃面'%name)
fork_lock.release()
noodle_lock.release() def eat2(name):
fork_lock.acquire()
print('%s拿到叉子了'%name)
time.sleep(1)
noodle_lock.acquire()
print('%s拿到面条啦'%name)
print('%s吃面'%name)
noodle_lock.release()
fork_lock.release() Thread(target=eat1,args=('alex',)).start()
Thread(target=eat2,args=('Egon',)).start()
Thread(target=eat1,args=('bossjin',)).start()
Thread(target=eat2,args=('nezha',)).start()

  

信号量和事件Event

信号量 :同一时间允许多个线程访问一段数据。

import time
from threading import Semaphore,Thread
def func(sem,a,b):
sem.acquire()
time.sleep()
print(a+b)
sem.release() sem = Semaphore()
for i in range():
t = Thread(target=func,args=(sem,i,i+))
t.start()

输出结果:


23
同时输出4个数据。

事件

# 事件被创建的时候
# False状态
# wait() 阻塞
# True状态
# wait() 非阻塞
# clear 设置状态为False
# set 设置状态为True

# 数据库 - 文件夹
# 文件夹里有好多excel表格
# 1.能够更方便的对数据进行增删改查
# 2.安全访问的机制

# 起两个线程
# 第一个线程 : 连接数据库
# 等待一个信号 告诉我我们之间的网络是通的
# 连接数据库
# 第二个线程 : 检测与数据库之间的网络是否连通
# time.sleep(0,2) 2
# 将事件的状态设置为True

import time
import random
from threading import Thread,Event
def connect_db(e):
count = 0
while count < 3:
e.wait(0.5) # 状态为False的时候,我只等待1s就结束
if e.is_set() == True:
print('连接数据库')
break
else:
count += 1
print('第%s次连接失败'%count)
else:
raise TimeoutError('数据库连接超时') def check_web(e):
time.sleep(random.randint(0,3))
e.set() e = Event()
t1 = Thread(target=connect_db,args=(e,))
t2 = Thread(target=check_web,args=(e,))
t1.start()
t2.start()

 条件condition(更为复杂的锁.)

acquire release  notify wait 四种方法.

一个条件被创建初,默认有一个false

notify 和wait 必须在release 和acquire 之间。

# 条件
from threading import Condition # 条件
# 锁
# acquire release
# 一个条件被创建之初 默认有一个False状态
# False状态 会影响wait一直处于等待状态
# notify(int数据类型) 造钥匙
from threading import Thread,Condition
def func(con,i):
con.acquire()
con.wait() # 等钥匙
print('在第%s个循环里'%i)
con.release()
con = Condition()
for i in range(10):
Thread(target=func,args = (con,i)).start()
while True:
num = int(input('>>>'))
con.acquire()
con.notify(num) # 造钥匙
con.release()

  定时器 Timer)

import time
from threading import Timer
def func():
print('时间同步') #1-3 while True:
t = Timer(5,func).start() # 非阻塞的
time.sleep(5)
等待五秒执行func里的代码

  

Python标准模块--concurrent.futures

import time
from concurrent.futures import ThreadPoolExecutor
def func(n):
time.sleep(2)
print(n)
return n*n def call_back(m):
print('结果是 %s'%m.result()) tpool = ThreadPoolExecutor(max_workers=5) # 默认 不要超过cpu个数*5
for i in range(20):
tpool.submit(func,i).add_done_callback(call_back) # tpool.map(func,range(20)) # 拿不到返回值
# t_lst = []
# for i in range(20):
# t = tpool.submit(func,i)
# t_lst.append(t)
# tpool.shutdown() # close+join #
# print('主线程')
# for t in t_lst:print('***',t.result()) # ftp
# 并发编程

  

队列

Queue

q =queue.Queue() 队列先进先出

import queue

q=queue.Queue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())

  输出结果.

first
second
third

  

q = queue.LifoQueue() 栈 ,先进后出

import queue

q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third') print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''

优先级多列 

q= queue.PriorityQueue()

import queue
q =queue.PriorityQueue()
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))
q.put((-5,'d'))
q.put((1,'?'))
print(q.get())

结果:

D:\PycharmProjects\test\venv\Scripts\python.exe "D:/parcharm/12/Day 30/2.py"
(-5, 'd')

  

Day 41 线程的更多相关文章

  1. 4-1 线程安全性-原子性-atomic-1

    我们发现在不做任何同步的情况下,我们计算的累加结果是错误的. com.mmall.concurrency.example.count.CountExample2 C:\Users\ZHONGZHENH ...

  2. Java线程:概念与原理

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  3. Java线程:线程状态的转换

    Java线程:线程状态的转换   一.线程状态   线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞.用一个图来描述如下:   1.新状态:线程对象已 ...

  4. java线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  5. Java线程详解----借鉴

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  6. java 线程(1)

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  7. Java 线程的创建和启动

    Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例.每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码). Java 使用线程执 ...

  8. java线程大全一讲通

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  9. Python_oldboy_自动化运维之路_线程,进程,协程(十一)

    本节内容: 线程 进程 协程 IO多路复用 自定义异步非阻塞的框架 线程和进程的介绍: 举个例子,拿甄嬛传举列线程和进程的关系: 总结:1.工作最小单元是线程,进程说白了就是提供资源的 2.一个应用程 ...

随机推荐

  1. session以及分布式服务器session共享

    一.session的本质 http协议是无状态的,即你连续访问某个网页100次和访问1次对服务器来说是没有区别对待的,因为它记不住你. 那么,在一些场合,确实需要服务器记住当前用户怎么办?比如用户登录 ...

  2. 【原创】有关Silverlight中异常“XmalParseEception” 通用解决思路

    针对于 此类 XamlParse问题,大部分都是 silverlight/WPF 前段 xmal文件问题,仔细逐行审查 接口解决.

  3. 获取url路径中的参数

    简介 运用js的时候,我们有时可能会有这样的需求,就是想要获取浏览器地址栏指定的一项参数,形如:https://i.cnblogs.com/EditPosts.aspx?postid=8628413& ...

  4. 前端学习之JavaScript

    JavaScript的历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase.(客户端执行的语言) Net ...

  5. IDEA 的VM Option设置加快页面的加载速度

    VM Option的设置: -Xms1024M -Xmx2048M -XX:PermSize=128M -XX:MaxPermSize=256M

  6. Codeforces 679B. Barnicle 模拟

    B. Barnicle time limit per test: 1 second memory limit per test :256 megabytes input: standard input ...

  7. Codeforces 660A. Co-prime Array 最大公约数

    A. Co-prime Array time limit per test 1 second memory limit per test 256 megabytes input standard in ...

  8. PS大神的作品,每张都是科幻大片!

    相信大家在网上一定见过 各种PS的作品 但是要想成为“PS大神”, 不仅仅要会P图, 最关键的就是脑洞! 同样的马路破坏效果 在大神操作后变成了大片! 摩托车换成了骏马 这效果果然不一般! 这个绝对牛 ...

  9. [SoapUI] 配置默认环境的properties

    <Envs> <Env id="Live,Default environment"> <Project> <CusProperty nam ...

  10. 深入浅出 JMS(四) - ActiveMQ 消息存储

    深入浅出 JMS(四) - ActiveMQ 消息存储 一.消息的存储方式 ActiveMQ 支持 JMS 规范中的持久化消息与非持久化消息 持久化消息通常用于不管是否消费者在线,它们都会保证消息会被 ...