044 Python语法之多线程

线程

  1. 线程就是一堆指令,是CPU调度的最小单位
  2. 每个程序的内存都是独立的
  3. 线程的存在就是让程序并发运行
  4. 一个线程可以控制和操作其他线程

线程的格式

import _thread 多线程包
import win32api

def show(i):
    win32api.MessageBox(0, "我是内容", "我是标题", 1)

# 元组用于传递参数
_thread.start_new_thread(show, (i,))

主线程与子线程

  1. 主线程死了,子线程也会死
  2. 后台线程可以用while True进行主线程阻塞

全局解释器锁GIL(Global Interpreter Lock)

  1. 某一时间点只有一个线程在执行、CPython特性

线程冲突(线程安全问题)

import _thread

num=0
def add():
    global num
    for i in range(1000000):
        num+=1
    print(num)

for i in range(5):
    _thread.start_new_thread(add,())

while True:
    pass

线程冲突产生原因

  1. 多个线程存在
  2. 多个线程操作同一资源

基于类实现多线程

import threading
import time
import win32api

class Mythread(threading.Thread):
    def run(self):
        win32api.MessageBox(0, "我是内容", "我是标题", 1)


for i in range(5):
    t=Mythread()    # 初始化
    t.start()       # 开启

join方法

import threading
import time
import win32api

class Mythread(threading.Thread):
    def run(self):
        win32api.MessageBox(0, "我是内容", "我是标题", 1)

myThreadList=[]
for i in range(5):
    t=Mythread()    # 初始化
    t.start()       # 开启
    myThreadList.append(t)

for thd in myThreadList:
    thd.join()

join方法介绍

  1. 让主线程等待该线程,相当于阻塞主线程

同步锁机制

mutex.acquire(1)    # 锁住成功继续干活,没有锁住就一直等待

'''
会造成线程安全问题的代码
'''

mutex.release() # 释放锁

锁(互斥锁)

  1. 某段代码某时间只有持有锁的线程才能执行
  2. mutex.acquire():使用锁
  3. mutex.release():关闭锁

如何解决死锁问题

mutex.acquire()
mutex2.acquire()

mutex2.release()
mutex.release()

RLOCK:递归锁(为了解决单线程死锁问题)

locks = {
    lock1:key1;
    lock2:key2;
    lock3:key3;
}

创建线程的三种风格

第一种(函数方式)

import _thread 多线程包
import win32api

def show(i):
    win32api.MessageBox(0, "我是内容", "我是标题", 1)

# 元组用于传递参数
_thread.start_new_thread(show, (i,))

第二种(类方式)

import threading
import time
import win32api

class Mythread(threading.Thread):

    def run(self):
        win32api.MessageBox(0, "我是内容", "我是标题", 1)


for i in range(5):
    t=Mythread()    # 初始化
    t.start()       # 开启

第三种(匿名方式)

threading.Thread(target=show,args=()).start()
threading.Thread(target=show,args=()).start()
threading.Thread(target=show,args=()).start()

限制线程数量

# 相当于锁,只不过这个是限制执行该代码的线程数量
sem = threading.Semaphore(2)
sem = threading.BoundedSemaphore(2)     # 两个方法差不多
sem.acquire()
代码
sem.release()



import threading, time

# 限制线程数量为2个
sem = threading.Semaphore(2)

def gothread():
    with sem:   # 锁定某段代码所能容纳线程数量
        for i in range(10):
            print(threading.current_thread().name, i)
            time.sleep(1)

for i in range(5):
    threading.Thread(target=gothread).start()

线程数量

  1. sem = threading.Semaphore(2)
  2. sem = threading.BoundedSemaphore(2) # 两个方法差不多
  3. 没有足够的线程也会执行,执行完不会等待线程一起结束,谁执行完成谁先结束

凑固定数量线程执行

import threading, time

bar=threading.Barrier(3)    # 等待3个线程一起才执行

def sever():
    bar.wait()
    print(threading.current_thread().name, "start")
    time.sleep(5)
    print(threading.current_thread().name, "end")

for i in range(5):
    threading.Thread(target=sever).start()

Barrier方法介绍

  1. bar.wait()方法执行必须等到设置数量的线程一起才会一起执行

线程间通信Event

import threading
import time

def goevent():
    e=threading.Event() # 事件
    def go():
        e.wait()
        e.clear()
        print("go")
    threading.Thread(target=go).start()# 开启一个线程
    return e

t=goevent()
time.sleep(25)
t.set() # 激发事件

线程间通信介绍

  1. 事件:e.threading.Event() # 创建事件
  2. 等待:e.wait() 没有走set就会一直等待,每一个wait都需要一个set激发
  3. 清理:e.clear() 重置等待,不清理下次就不会等待了,会直接执行
  4. 激发:e.set()这一步执行完成之后,e.wait()等待才会继续
  5. 判断是否激发事件:e.is_set()返回是否激发事件的状态

线程通信与事件Condition

import threading, time


def go1():
    with cond:
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().getName(), i)
        cond.notify()  # 通知


def go2():
    with cond:
        for i in range(10):
            time.sleep(1)
            print(threading.current_thread().getName(), i)
            if i==5:
                cond.wait()  # 通知

cond = threading.Condition()
threading.Thread(target=go2).start()
threading.Thread(target=go1).start()

线程通信与事件 Condition=LOCK+Event

  1. 线程条件变量
  2. cond.notify() # 通知,不会通知自己
  3. cond.wait() # 等待通知
  4. wait必须有同一个notify通知之后才会走

生产者消费者模式

生产者消费者模式代码

import threading
import time
import queue
import random

class createThread(threading.Thread):
    def __init__(self,index,myqueue):
        threading.Thread.__init__(self)
        self.index = index
        self.myqueue = myqueue

    def run(self):
        while True:
            time.sleep(3)# 3秒生产一个
            num = random.randint(1,1000000) # 随机数
            self.myqueue.put("input 生产者"+str(self.index)+"硅胶娃娃"+str(num)+"号")
            print("input 生产者"+str(self.index)+"硅胶娃娃"+str(num)+"号")
        self.myqueue.task_done()


class buyThread(threading.Thread):
    def __init__(self,index,myqueue):
        threading.Thread.__init__(self)
        self.index = index
        self.myqueue = myqueue

    def run(self):
        while True:
            time.sleep(1)
            item = self.myqueue.get()   # 抓取一个数据
            if item is None:
                break
            print("客户",self.index,"买到物品",item)
        self.myqueue.task_done()

myqueue = queue.Queue(10)   # 最大容量为10个
for i in range(3):
    createThread(i,myqueue).start() # 生产者

for i in range(8):
    buyThread(i,myqueue).start()

生产者消费者模式分析

  1. 生产的速度可以小于于消费的速度
  2. 生产了一个才能消费一个
  3. 不会出现线程安全问题

线程池

import time
import threadpool

def show(str):
    print("hello", str)
    time.sleep(2)

nameList = ["hsgj","hsfdh","hsdh","ffdg"]
start_time = time.time()

pool = threadpool.ThreadPool(10)    # 最大容量10个
requests = threadpool.makeRequests(show,nameList)   # 构建请求列表: 第一个函数,第二个参数列表
for req in requests:
    pool.putRequest(req)    # 将请求压入线程池中执行

end_time = time.time()
print(end_time-start_time)

定时任务(定时线程)

import os
import threading

import time


def go():
    os.system("start notepad")


timeThread = threading.Timer(5, go)     # 延时5秒后执行
timeThread.start()
i = 0
while True:
    time.sleep(1)
    print(i)
    i += 1

定时任务

  1. threading.Timer(5, go).start()
  2. 第一个参数定时秒数
  3. 第二个参数要执行的方法

with在锁中的作用

with作用

lock = threading.Lock() # 创建一个锁

with lock:
    pass

with解释

  1. with会自动打开关闭锁

前台与后台线程(守护线程)

上代码

import threading
import tkinter.messagebox

class MyThread(threading.Thread):
    def run(self):
        # tkinter.messagebox.showerror(title="错误",message="错误")
        for i in range(100):
            print(i)

t = MyThread()
t.setDaemon(True)   # 设置线程为后台线程
t.start()

前台与后台线程(守护线程)介绍

  1. threading.Thread默认是前台线程
  2. 主线程不会等待后台线程(守护线程)
  3. setDaemon必须放在start之前
  4. _thread是后台线程

每个线程独立存储

import threading
import time

data = threading.local() # 为每个线程独立存储空间
t1 = lambda x:x+1
t2 = lambda x:x+"1"
def printdata(func,x):
    data.x = x
    print(id(data.x))   # 不同的地址
    for i in rang(5):
        data.x=func(data.x)
        print(threading.current_thread().name,data.x)
        
        
threading.Thread(target=printdata,args=(t1,1)).start()
threading.Thread(target=printdata,args=(t2,"1")).start()

每个线程独立存储

import threading

# 创建全局 ThreadLocal 对象:  
localVal = threading.local()
localVal.val = "Main-Thread"


def process_student():
    print('%s (in %s)' % (localVal.val, threading.current_thread().name))


def process_thread(name):
    # 赋值
    localVal.val = name
    process_student()


t1 = threading.Thread(target=process_thread, args=('One',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('Two',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
print(localVal.val)
发布了151 篇原创文章 · 获赞 26 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/runnoob_1115/article/details/102952427
044
今日推荐