并发编程 线程

1.线程的概念:

  1. 什么是线程: 进程和线程都是虚拟单位,都是用来帮助我木门形象的描述某种事物

    进程 : 资源单位    线程:执行单位

    每一个进程都自带一个线程,线程才是真正的执行单位,进程只是在线程运行过程中提供代码运行所需要的资源

 

  2. 为什么要有线程:

    开进程 1 申请内存空间 耗资源,  拷贝代码, 耗资源   

    开线程  一个进程内可以起多个线程,并且线程与线程之间的数据是共享的

    开启线程的开销要远远小于开启进程的开销

2.Threading.Thread 创建线程模块

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

 

3.代码的实现

  1.创建线程的两种方式

from threading import Thread
import time

def func(name):
    print(f'{name}线程正在创建')
    time.sleep(2)
    print(f'{name}线程结束')
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
if __name__ == '__main__':
    t = Thread(target=func,args=('小明',))
    t.start()
    print('')
from threading import Thread
import time
class MyThread(Thread):
    def __init__(self,name):
        super().__init__()
        self.name = name

    def run(self):
        print(f'{self.name}线程正在创建')
        time.sleep(2)
        print(f'{self.name}线程结束')


if __name__ == '__main__':
    t = MyThread('小明')
    t.start()
    print('')

  2.线程对象及其他方法:

from threading import Thread,current_thread,active_count
import time
import os

def task(name,i):
    print('%s is running'%name)
    # print('子current_thread:',current_thread().name)
    # print('子',os.getpid())
    time.sleep(i)

    print('%s is over'%name)
# 开线程不需要在__main__代码块内 但是习惯性的还是写在__main__代码块内
t = Thread(target=task,args=('egon',1))
t1 = Thread(target=task,args=('jason',2))
t.start()  # 告诉操作系统开辟一个线程  线程的开销远远小于进程
t1.start()  # 告诉操作系统开辟一个线
t1.join()  # 主线程等待子线程运行完毕
print('当前正在活跃的线程数',active_count())
# 小的代码执行完 线程就已经开启了
print('')
# print('主current_thread:',current_thread().name)  # 线程名字
# print('主',os.getpid())

   3. 守护线程:与进程方法相同,但需要注意的是:主线程的结束也就以为着进程的结束,主线程必须等待其他非守护线程的结束才能结束,也就是意味着子线程在运行的时候需要使用进程中的资源,而主线程一旦结束了资源也就销毁了

from threading import Thread,current_thread
import time

def task(i):
    print(current_thread().name)
    time.sleep(i)
    print('GG')
t = Thread(target=task,args=(1,))
t.daemon = True
t.start()
print('')   
'''
Thread-1
主     因为t线程为守护进程,主线程运行结束t线程也跟着结束
'''

  4.  同一进程中线程之间的数据是共享的

from threading import Thread

money = 666
def task():
    global money
    money = 999

t = Thread(target=task)
t.start()
t.join()
print(money)   # 999 同一进程中线程间的数据是共享的

  5.  线程中的互斥锁与进程中的使用方法一致

from threading import Thread,Lock
import time

n = 100

def task(mutex):
    global  n
    mutex.acquire()
    tmp = n
    time.sleep(0.1)
    n = tmp - 1
    mutex.release()

t_list = []
mutex = Lock()
for i in range(100):
    t = Thread(target=task,args=(mutex,))  # 创建100个线程
    t.start()  
    t_list.append(t)  # 将线程对象添加到列表中
for t in t_list:  # 遍历线程对象列表
    t.join()   # 等待所有线程程运行结束再执行主线程
print(n)   # 0 

  6. 用线程实现sockte服务端的并发:

    服务端:

import socket
from threading import Thread
def talk(conn):
    while 1:
        try:
            ret = conn.recv(1024)
            if len(ret) == 0: break
            print(ret)
            conn.send(ret.upper())
        except ConnectionResetError:
            break
    conn.close()


sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()

while 1:
    conn,addr = sk.accept()
    t = Thread(target=talk,args=(conn,))
    t.start()

    客户端:

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
while 1:
    sk.send(b'hello')
    ret = sk.recv(1024)
    print(ret)

  7: GIL全局解释器锁:

  

"""
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
native threads from executing Python bytecodes at once. This lock is necessary mainly
because CPython’s memory management is not thread-safe.
"""

ps:python解释器有很多种 最常见的就是Cpython解释器
  GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全 他是用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)GIL的存在是因为CPython解释器的内存管理不是线程安全的,也就是说同一个进程中的多个线程再同一时间只有一个线程被cpu执行,GIL全局解释器锁只是针对线程的,进入IO自动释放

  总结.在执行多个计算密集型的任务时.在计算机单核的情况下开线程最好,在多核情况下开进程会效率更高

    .在执行多个IO密集型的任务时.开线程时最好的

  8:死锁与递归锁:当一个进程或线程中同时出现多把锁容易造成死锁显现,程序卡死

   递归锁: Rlock,可以多次acquire与release  当一个线程或进程中所有acquire都被release,其他的线程才能获得资源,注意使用递归锁时是链式赋值

    科学家吃面示例:

import time
from threading import Thread,RLock
lock1 = lock2 = RLock()
def eat1(name):
    lock2.acquire()
    print('%s 抢到了面条'%name)
    lock1.acquire()
    print('%s 抢到了叉子'%name)
    print('%s 吃面'%name)
    lock1.release()
    lock2.release()

def eat2(name):
    lock1.acquire()
    print('%s 抢到了叉子' % name)
    time.sleep(1)
    lock2.acquire()
    print('%s 抢到了面条' % name)
    print('%s 吃面' % name)
    lock2.release()
    lock1.release()

for name in ['小明','小黄','小亮']:
    t1 = Thread(target=eat1,args=(name,))
    t2 = Thread(target=eat2,args=(name,))
    t1.start()
    t2.start()

 

  

  9 信号量: Semaphore: 用锁的原理实现的,内置了一个计数器,在同一时间只能有指定数量的进程或线程执行某一段代码,如果把互斥锁比作一个车位的话,那么信号量相当于多个车位

  

from threading import Thread,Semaphore  # 线程使用信号量导入方法
# from multiprocessing import Process,Semaphore  # 进程使用信号量导入方法
import time
import random

sm = Semaphore(5)  # 5个车位

def func(name):
    sm.acquire()  # 占车位
    print(f'{name}占了一个车位')
    time.sleep(random.randint(1,2))
    sm.release()  # 让出车位
    print(f'{name}让了一个车位')

for i in range(20):
    t = Thread(target=func,args=(i,))
    t.start()
'''同时占用车位最多不会超过5个,一个让出去另一个才会占用
'''

  10:事件:event类似于join 不过事件是在处理两个子进程或子线程之间互相等待的过程

    常用方法:

from multiprocessing import Event
e = Event()
e.wait()  # 阻塞态,状态为True不堵塞,为False 堵塞
e.is_set()  # 查看状态 
e.set()  # False  ---> True
e.clear()  # True ---> False

    红绿灯示例:

from threading import Event,Thread
import time
e = Event()

def light():
    print('红灯亮了')
    time.sleep(3)
    e.set()
    print('绿灯亮了')

def car(name):
    print(f'{name}正在等红灯')
    e.wait()
    print(f'{name}可以行驶了')

l = Thread(target=light)
l.start()
for i in range(10):
    c = Thread(target=car,args=(i,))
    c.start()

  11. 线程队列: 

    1: queue  队列 

import queue
q = queue.Queue()
q.put(1)
q.put(2)
print(q.get())  # 1
print(q.get())  # 2

    2: Lifoqueue 堆栈

import queue
q = queue.LifoQueue()  # 堆栈
q.put(1)
q.put(2)
print(q.get())  # 2
print(q.get())  # 1

    3: PriorityQueue  优先级队列

import queue
q = queue.PriorityQueue() # 堆栈
q.put((1,'a'))  # 放一个元组,第一个元素是优先级为int型,数字越小优先级越高
q.put((0,'b'))  
q.put((-1,'c'))
q.put((1,'d'))  # 同一优先级,按照内容对应的ASCII码表来确认优先级
print(q.get())  # (-1, 'c')
print(q.get()) # (0, 'b')
print(q.get()) # (1, 'a')
print(q.get())  # (1, 'd')

 

posted @ 2019-08-12 19:43  人生能有几回搏  阅读(152)  评论(0编辑  收藏  举报