1

Day 41 线程

 

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

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

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

因此在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(2) 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(2)
        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(111)
    #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 = 0
    print(g,os.getpid())

g = 100
t_lst = []
for i in range(10):
    t = Thread(target=func,args=(i,5))
    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('*'*10)
        time.sleep(1)
def func2():
    print('in func2')
    time.sleep(5)

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('*'*10)
        time.sleep(1)
def func2():
    print('in func2')
    time.sleep(5)

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(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()

 

 

 

输出结果 

 

 

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(1)
    print(a+b)
    sem.release()

sem = Semaphore(4)
for i in range(10):
    t = Thread(target=func,args=(sem,i,i+5))
    t.start()

 

输出结果:

7
9
5
11
13
15
17
19
21
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')

  

 

posted @ 2018-03-30 10:17  萌哥-爱学习  阅读(196)  评论(0编辑  收藏  举报