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

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

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

因此在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. Golang之时间、日期类型

    孤身只影的一直小地鼠,艰难的走在路上 package main import ( "fmt" "time" ) //获取时间的格式 func testTime( ...

  2. 白盒静态自动化测试工具:FindBugs使用指南

    目 录     1     FINDBUGS介绍     2     在ECLIPSE中安装FINDBUGS插件     3     在ECLIPSE中使用FINDBUGS操作步骤     3.1   ...

  3. 怎样完整地离线更新并升级基于 Debian 的操作系统

    不久之前我已经向你展示了如何在任意离线的 Ubuntu 和 Arch Linux 操作系统上安装软件. 今天,我们将会看看如何完整地离线更新并升级基于 Debian 的操作系统. 和之前所述方法的不同 ...

  4. linux下第一个C程序

    首先,用vi编辑器新建一个文件 $vi hi.c 输入以下的程序(怎么用vi不说了) #include <stdio.h> int main() { printf("hello. ...

  5. Devexpress VCL Build v2014 vol 14.2.1 beta发布

    已经快到2015 年了. 14.2.1 beta 才出来了. 还好,有一些新东西. 官网地址 VCL Gauge Control Designed to clearly convey informat ...

  6. kbmMWEncodeEscapes 中汉字编码的问题及解决办法

    kbmMWEncodeEscapes 是kbmmw 里面的一个函数,用来对URL 中的汉字进行编码,例如 http://127.0.0.1/getname?name=春节,由于'春节'是汉字,浏览器向 ...

  7. hdu-1166(线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166 思路:线段树模板 #include<iostream> #include<cs ...

  8. chandy-lamport 分布式一致性快照 算法详细介绍

    在一个分布式计算系统中,为了保证数据的一致性需要对数据进行一致性快照.Flink和spark在做流失计算的时候都借鉴了chandy-lamport算法的原理,这篇文章就是对chandy-lamport ...

  9. 学习前端的菜鸡对JS的call,apply,bind的通俗易懂理解

       call,apply,bind 在JavaScript中,call.apply和bind是Function对象自带的三个方法,都是为了改变函数体内部 this 的指向.            a ...

  10. UltraEdit配置

    1.如何在vivado中调用UltraEdit 1.语法高亮 支持不同的编程语言,但是要添加相就的文件,这样不同语言的关键字就可以高亮显示. 在高级-> 配置 –> 语法高亮,选择文档 2 ...