并发编程|线程

线程

  进程是资源的单位,而线程是执行单位。开启进程需要内存空间,这就好比造一个生产车间,好比进程内地代码就是生产资料,有变量,有函数,有类等等。而进程就是生产线,把这些生产资料放在线程这条生产线,才能生产出结果。每个进程都自带线程,线程才是真正的执行单位,进程为线程提供资源。

  线程与进程(进程:https://www.cnblogs.com/huaiXin/p/11329260.html)。进行比较,开启进程需要申请内存空间,还需要‘拷贝’主进程的代码。但是开启线程不需要申请内存空间。一个进程内可以有多个线程,并且线程与线程之间是数据共享的。

开启现成的两种方式

  开启线程需要引入threading模块中的Thread这个类。开启方式一种是引用Thread类开启,另一种继承Thread类,自定义一个类开启。

'''
开启线程的第一种方式
'''
from threading import Thread  # 调用线程模块
import time

def task(name):
    print(f'{name}来了')
    time.sleep(3)
    print(f"{name}结束了")

t = Thread(target=task, args=('旷哥',))   # 创建线程对象
t.start()       # 启动线程
print('')
'''
开启线程的第二种方式
'''
from threading import Thread
import time
class MyThread(Thread):   # 继承Thread这个类
    def __init__(self, name):
        super().__init__()
        self.name = name
    def run(self):           #  写run方法
        print(f'{self.name}来了')
        time.sleep(2)
        print(f'{self.name}结束')

t = MyThread('旷哥')
t.start()
print('')

线程对象及其他方法

'''
current_thread : 查看线程对象方法
os.getpid: 查看PID端口号
active_count: 查看当前活跃的线程数
t.daemon: 设置伴随进程
'''

from threading import Thread, current_thread ,active_count # 调用线程模块
import time
import os

def task(name):
    print(f'{name}来了')
    print('',os.getpid())   # 查看PID端口号
    print('current_thread:', current_thread().name)
    time.sleep(3)
    print(f"{name}结束了")

t = Thread(target=task, args=('旷哥',))   # 创建线程对象
t.daemon = True
t.start()       # 启动线程
print('',os.getpid())
print(active_count())   # 查看当前活跃的线程数
print('',current_thread().name)

线程互斥锁  

  当多个线程同时操作统一数据,数据是不安全的,需要加锁处理,讲线程并行操作数据改为串行操作数据。 

'''
线程互斥锁
    当多个线程同时操作统一数据,数据是不安全的
    需要加锁,讲线程并行操作数据改为串行操作数据
'''
from threading import Thread, Lock
import time

n = 100
def func(L):
    global n
    L.acquire()  # 抢锁
    temp = n
    time.sleep(0.1)  # 模拟网络延迟
    temp -= 1
    n = temp
    L.release()  # 放锁

t_list = []
numtext = Lock()
for i in range(100):    # 开启100个线程
    t = Thread(target=func, args=(numtext,))
    t.start()
    t_list.append(t)
for k in t_list:
    k.join()  # 等待所有线程结束
print(n)  # 打印共享的N

 GIL全局解释器锁

  GLI(global interproter Lock)是cpython全局解释器锁。它的本质是一把互斥锁,将并发变成串行牺牲效率保证安全性。cpython中每一个进程都有自身的线程,python中的垃圾回收机制,也是一行代码和模块,启动python是也需要启动垃圾回收机制代码,这也需要一个线程。它们都需要python解释器经行编译运行,而每个进程只有一个python解释器,进程下的所有线程都共用这个解释器,为了保证线程安全运行,就需要GIL对线程进行保护。GIL的特点:单进程下无法利用多核优势。而且多不同的数据加载不同的锁处理。

  python开多线程多进程,视情况而定。当计算密集型时多任务,单核条件下应使用开多线程:多核条件下计算量在较小时开启多线程,如果不是就开启多进程。当I/O密集型多任务是无论单核多核,开启多线程。

'''
计算密集型
    总结: 计算密集型在单核条件下。应开线程, 多核条件下, 在计算量百万次一下开线程,以上开进程
'''
import time
from multiprocessing import Process
from threading import Thread
import os

def sun():
    i = 0
    while i < 1e5:
        i += 1

if __name__ == '__main__':
    p_list = []
    print(os.cpu_count())
    start = time.time()
    for i in range(4):
        # p = Process(target=sun,)  # 0.9663281440734863
        p = Thread(target=sun,)     # 0.37197113037109375
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(f'用时{time.time()-start}')
'''
I/O密集型
    总结:
        单核与多核条件开启线程
'''
import time
from multiprocessing import Process
from threading import Thread
import os

def sun():
    i = 0
    while i < 1e3:
        time.sleep(0.1)
        i += 1

if __name__ == '__main__':
    p_list = []
    print(os.cpu_count())
    start = time.time()
    for i in range(4):
        # p = Process(target=sun,)  # 11.024189472198486
        p = Thread(target=sun,)     # 10.05493450164795
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print(f'用时{time.time()-start}')
线程与进程优势

死锁现象与递归锁

  当使用锁过多时,可能会出现死锁现象。

'''
死锁现象
'''
import time
from threading import Thread, Lock

muxB = Lock()
muxA = Lock()
class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()
        pass
    def func1(self):
        muxA.acquire()
        print(f'{self.name}抢到A锁')
        muxB.acquire()
        print(f'{self.name}抢到B锁')
        muxB.release()
        print(f'{self.name}释放B锁')
        muxA.release()
        print(f'{self.name}释放A锁')

    def func2(self):
        muxB.acquire()
        print(f'{self.name}抢到B锁')
        time.sleep(0.1)
        muxA.acquire()
        print(f'{self.name}抢到A锁')
        muxA.release()
        print(f'{self.name}释放A锁')
        muxB.release()
        print(f'{self.name}释放B锁')
for i in range(10):
    t = MyThread()
    t.start()
# 结果
Thread-1抢到A锁
Thread-1抢到B锁
Thread-1释放B锁
Thread-1释放A锁
Thread-2抢到A锁
Thread-1抢到B锁   # 卡在此处
死锁现象

  递归锁

  递归锁是threading模块下RLock。这中锁多个对象每抢一次锁,内部加1计数,每释放一次就减一。计数标志为0时,其他对象才可以抢锁。且共用一把锁。

'''
递归锁
'''

import time
from threading import Thread, RLock

muxB = muxA = RLock()

class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()
        pass
    def func1(self):
        muxA.acquire()
        print(f'{self.name}抢到A锁')
        muxB.acquire()
        print(f'{self.name}抢到B锁')
        muxB.release()
        print(f'{self.name}释放B锁')
        muxA.release()
        print(f'{self.name}释放A锁')

    def func2(self):
        muxB.acquire()
        print(f'{self.name}抢到B锁')
        time.sleep(0.1)
        muxA.acquire()
        print(f'{self.name}抢到A锁')
        muxA.release()
        print(f'{self.name}释放A锁')
        muxB.release()
        print(f'{self.name}释放B锁')
for i in range(10):
    t = MyThread()
    t.start()
递归锁

  信号量

  也是一种锁的应用。在threading模块下Semaphore方法。控制锁的个数。

import time
from threading import Thread, Semaphore

s = Semaphore(2)  # 控制锁的各所,同时有两个锁
n = 10
def func1(i):
    global n
    s.acquire()
    temp = n
    time.sleep(1)
    temp -= 1
    n = temp
    print(f'{i}操作后{n}')
    s.release()

for i in range(10):
    t = Thread(target=func1, args=(i,))
    t.start()
信号量

event事件

  在threading模块下的Event方法,使子进程之间可会互相等待。

import time
from  threading import Thread, Event

e = Event()

def func1():
    print('红灯亮起!')
    time.sleep(3)
    print('绿灯亮起!')
    e.set()

def func2(name):
    print(f'{name}准备!')
    e.wait()
    print(f'{name}出发!')

# 线程t1等待线程t
t = Thread(target=func1, )
t.start()
for i in range(10):
    t1 = Thread(target=func2, args=(f'车手{i}号',))
    t1.start()

  

原文地址:https://www.cnblogs.com/huaiXin/p/11340688.html