Thread:线程

了解线程
线程,也叫轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
(百度百科之定义)
Python中的多线程
Python中的多线程是假的多线程!因为Python代码的执行由Python解释器来控制,而对Python解释器的访问由全局解释器锁(GIL)来控制,这个锁保证了同一时间只有一个线程在执行。
在多线程的环境中,Python解释器以时间切片的方法,按以下方式运行:
1.设置GIL锁 (就绪状态)
2.切换线程,执行(运行状态)
3.运行结束,把线程休眠(阻塞状态)
4.解锁,并重复以上步骤
所以,多线程在Python中是交替执行的,单位时间内,不管是几核的系统,只能跑一个线程,然后时间片切换
创建一个线程

那么多线程怎么使用多核:
1、重写python编译器(官方cpython)如使用:PyPy解释器
2、调用C语言的链接库

import _thread
import threading
from threading import Thread

import time


def doSth(arg):
    # 拿到当前线程的名称和线程号id
    threadName = threading.current_thread().getName()
    tid = threading.current_thread().ident
    for i in range(5):
        print("%s *%d @%s,tid=%d" % (arg, i, threadName, tid))
        time.sleep(2)


# 使用_thread.start_new_thread开辟子线程
def simpleThread():
    # 创建子线程,执行doSth
    # 用这种方式创建的线程为【守护线程】(主线程死去“护卫”也随“主公”而去)
    _thread.start_new_thread(doSth, ("拍森",))

    mainThreadName = threading.current_thread().getName()
    print(threading.current_thread())
    # 5秒的时间以内,能看到主线程和子线程在并发打印
    for i in range(5):
        print("劳资是主线程@%s" % (mainThreadName))
        time.sleep(1)

    # 阻塞主线程,以使【守护线程】能够执行完毕
    while True:
        pass

# 通过创建threading.Thread对象实现子线程
def threadingThread():
    # 默认不是【守护线程】
    t = threading.Thread(target=doSth, args=("大王派我来巡山",))
    # t.setDaemon(True)  # 设置为守护线程
    t.start()  # 启动线程,调用run()方法

    # 5秒的时间以内,能看到主线程和子线程在并发打印
    for i in range(5):
        print("劳资是大王@%s" % (threading.current_thread().getName()))
        time.sleep(1)

# 继承于threading.Thread
class MyThread(threading.Thread):
    # 构造方法一定要执行super方法,否则不能构成事实上的线程类
    # 继承法的优势在于可以灵活自定义(构造方法传参,在类内部封装很多方法)
    # 注意:构造方法是跑在【主调线程】(创建当前实例的线程)
    def __init__(self, name, task, subtask):
        super().__init__()

        self.name = name  # 覆盖了父类的name
        self.task = task  # MyThread自己的属性
        self.subtask = subtask

    # 覆写父类的run方法,
    # run方法以内为【要跑在子线程内的业务逻辑】(thread.start()会触发的业务逻辑)
    def run(self):
        for i in range(5):
            print("【%s】并【%s】 *%d @%s" % (self.task, self.subtask, i, threading.current_thread().getName()))
            time.sleep(2)


# 通过继承threading.Thread类,进而创建对象实现子线程
def classThread():
    mt = MyThread("小分队I", "巡山", "扫黄")
    mt.start()
    for i in range(5):
        tid = threading.current_thread().ident
        print("劳资是大王@%s,tid=%d" % (threading.current_thread().getName(), tid))
        time.sleep(1)
    print("主线程over")


# 几个重要的API
def importantAPI():
    print(threading.currentThread())  # 返回当前的线程变量
    # 创建五条子线程
    t1 = threading.Thread(target=doSth, args=("巡山",))
    t2 = threading.Thread(target=doSth, args=("巡水",))
    t3 = threading.Thread(target=doSth, args=("巡鸟",))

    t1.start()  # 开启线程
    t2.start()
    t3.start()

    print(t1.isAlive())  # 返回线程是否活动的
    print(t2.isDaemon())  # 是否是守护线程
    print(t3.getName())  # 返回线程名
    t3.setName("巡鸟")  # 设置线程名
    print(t3.getName())
    print(t3.ident)  # 返回线程号

    # 返回一个包含正在运行的线程的list
    tlist = threading.enumerate()
    print("当前活动线程:", tlist)

    # 返回正在运行的线程数量(在数值上等于len(tlist))
    count = threading.active_count()
    print("当前活动线程有%d条" % (count))


if __name__ == '__main__':
    simpleThread()
    # threadingThread()
    # classThread()
    # importantAPI()
    pass

创建一个多线程

'''
少林功夫+唱歌+跳舞
'''
import threading
import random

# 功夫、铁头功+金刚腿
import time


def kongfu(kind):
    print("我是%s" % kind)
    
# 唱歌
def sing(who, what):
    # time.sleep(3)
    print("%s:%s" % (who, what))
# 跳舞
def dance():
    time.sleep(1)
    print("蹦瞎卡拉卡,蹦瞎卡拉卡")

if __name__ == '__main__':
    singList = ["少林功夫好耶", "真滴好啊", "少林功夫够劲", "系好劲", "我系铁头功", "无敌铁头功",
                "我系金刚腿", "金刚腿", "他系铁头功", "哦哇,哦哇........"]
    t1 = threading.Thread(target=kongfu, args=("铁头功",))
    t2 = threading.Thread(target=kongfu, args=("金刚腿",))
    for i in range(len(singList)):
        t3 = threading.Thread(target=sing, args=("周星星", random.choice(singList)))
        t4 = threading.Thread(target=sing, args=("大师兄", random.choice(singList)))

        t3.start()
        t3.join()
        t4.start()
        t4.join()
    t5 = threading.Thread(target=dance)

    # 线程同步(依次执行)
    t1.start()
    t2.start()
    t5.start()

    # join()等待线程执行完毕,
    # 线程异步(并发执行)
    t1.join()
    t2.join()
    t5.join()
    pass

线程冲突

'''
【线程冲突】示例:
多个线程并发访问同一个变量而互相干扰
互斥锁
    状态:锁定/非锁定
    #创建锁
        lock = threading.Lock()
    #锁定
        lock.acquire()
    #释放
        lock.release()
'''

import threading

import time
money = 0

# CPU分配的时间片不足以完成一百万次加法运算,
# 因此结果还没有被保存到内存中就被其它线程所打断
def addMoney():
    global money
    for i in range(1000000):
        money += 1
    print(money)


# 创建线程锁
lock = threading.Lock()

def addMoneyWithLock():
    print("addMoneyWithLock")
    time.sleep(1)
    global money
    # print(lock.acquire())
    # if lock.acquire():
    #     for i in range(1000000):
    #         money += 1
    # lock.release()
    # 独占线程锁
    with lock:  # 阻塞直到拿到线程锁

        # -----下面的代码只有拿到lock对象才能执行-----
        for i in range(1000000):
            money += 1
        # 释放线程锁,以使其它线程能够拿到并执行逻辑
        # ----------------锁已被释放-----------------

    print(money)


# 5条线程同时访问money变量,导致结果不正确
def conflictDemo():
    for i in range(5):
        t = threading.Thread(target=addMoney)
        t.start()


# 通过线程同步(依次执行)解决线程冲突
def handleConflictBySync():
    for i in range(5):
        t = threading.Thread(target=addMoney)
        t.start()
        t.join()  # 一直阻塞到t运行完毕


# 通过依次独占线程锁解决线程冲突
def handleConflictByLock():
    # 并发5条线程
    for i in range(5):
        t = threading.Thread(target=addMoneyWithLock)
        t.start()


if __name__ == '__main__':
    # conflictDemo()
    # handleConflictBySync()
    handleConflictByLock()
    # addMoneyWithLock()
    pass

死锁

'''
互相锁住对方线程需要的资源,造成死锁局面
'''

import threading
from threading import Thread
import time

# 自尊锁
boyHonor = threading.Lock()
girlHonor = threading.Lock()
honor = threading.RLock()


class Boy(Thread):
    def run(self):
        print("妈蛋气死劳资了战斗开始...")

        # 锁住Girl所需的boyHonor对象
        if boyHonor.acquire():
            print("Boy:Gril必须先道歉!")
            time.sleep(1)

            # 需要girlHonor才能继续执行
            if girlHonor.acquire(timeout=-1):
                girlHonor.release()
                boyHonor.release()
                print("Boy:im sorry too!")
            else:
                print("Boy:....")
        print("Boy战斗结束")


class Girl(Thread):
    def run(self):
        print("妈蛋气死老娘了战斗开始...")

        # 锁住Boy所需的girlHonor对象
        if girlHonor.acquire():
            print("Gril:Boy必须先道歉!")

            # 需要boyHonor才能继续执行
            if boyHonor.acquire(timeout=-1):
                boyHonor.release()
                girlHonor.release()
                print("Girl:im sorry too!")
            else:
                print("Girl:妈蛋分手!")
        print("Girl战斗结束")

if __name__ == '__main__':
    Boy().start()
    Girl().start()
    pass

递归锁

'''
互相锁住对方线程需要的资源,造成死锁局面
递归锁,用于解决死锁的问题,可重复锁
'''

import threading
from threading import Thread
import time

# 自尊锁
honor = threading.RLock()


class Boy(Thread):
    def run(self):
        print("妈蛋气死劳资了战斗开始...")

        # 锁住Girl所需的honor对象
        if honor.acquire():
            print("Boy:Gril必须先道歉!")
            time.sleep(1)

            # 需要honor才能继续执行
            if honor.acquire(timeout=-1):
                honor.release()
                honor.release()
                print("Boy:im sorry too!")
            else:
                print("Boy:....")
        print("Boy战斗结束")


class Girl(Thread):
    def run(self):
        print("妈蛋气死老娘了战斗开始...")

        # 锁住Boy所需的honor对象
        if honor.acquire():
            time.sleep(2)
            print("Gril:Boy必须先道歉!")

            # 需要honor才能继续执行
            if honor.acquire(timeout=-1):
                honor.release()
                honor.release()
                print("Girl:im sorry too!")
            else:
                print("Girl:妈蛋分手!")
        print("Girl战斗结束")


if __name__ == '__main__':
    Boy().start()
    Girl().start()
    pass

event实现线程通信

'''
Event实现线程通信
'''
import threading

import time


def handleEvent(event):
    global data
    print("子线程正在循环监听事件...")
    tname = threading.current_thread().getName()

    while True:
        event.wait()  # 阻塞监听事件
        time.sleep(0.1)

        # 3.拿到事件数据并处理
        print("%s处理事件*%d" % (tname, data), event)

        event.clear()
        print("事件已被处理")

    pass


# 在栈内存中声明data
data = 0
if __name__ == '__main__':
    event = threading.Event()

    # 创建事件监听线程
    threading.Thread(target=handleEvent, args=(event,), name="foo").start()
    threading.Thread(target=handleEvent, args=(event,), name="bas").start()
    threading.Thread(target=handleEvent, args=(event,), name="pig").start()

    for i in range(10):
        # 1.改变数据
        # data = 1 #重新声明局部变量data
        data += 1

        # 2.发布事件,通知数据已改变
        event.set()
        print("\n事件已发布*%d\n" % (data))

        time.sleep(1)
    pass

condition实现线程通信

'''
通过threading.Condition实现线程通信:
生产消费模型
'''
import random
import threading

import time

# 线程间交替通信的信物
condition = threading.Condition()

# 产品容器
plist = []


# 产品
class Product:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "Product:%s" % (self.name)


# 生产者线程
class Producer(threading.Thread):
    def run(self):
        while True:
            # 阻塞等待获得condition对象
            with condition:
                # 此处已经获取到condition对象(condition.acquire())
                print("生产者拿到condition")

                p = Product(random.randint(100, 999))
                plist.append(p)
                print("生产者生产了", p)
                time.sleep(1)

                condition.notify()  # 通知消费者,谁wait()了就通知谁
                condition.wait()  # 监听消费者通知,谁wait代表谁希望被notify(wait中会释放condition)

                # with走完,交出condition
                # 此处condition已释放(condition.release())


# 消费者线程
class Consumer(threading.Thread):
    def run(self):
        while True:

            # 阻塞等待获得condition对象(本例中一直等到生产者with condition内的代码走完)
            with condition:
                # 此处已经获取到condition对象(condition.acquire())
                print("消费者拿到conditon")
                try:
                    p = plist.pop()
                    print("%s消费者消费了" % (self.name), p)

                    condition.notify()  # 通知生产者生产,谁with了相同condition且wait就通知谁
                    condition.wait()  # 等候生产者消息(wait中会释放condition)
                    print("fuck")
                except IndexError:
                    print("消费者:冰箱里空空如也")
                    # 此处condition已释放(condition.release())


if __name__ == '__main__':
    Producer().start()
    Consumer().start()
    pass

condition实现线程通信2

'''
通过threading.Condition实现线程通信:
生产消费模型
'''
import random
import threading

import time

# 线程间交替通信的信物
condition = threading.Condition()

# 产品容器
plist = []


# 产品
class Product:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "Product:%s" % (self.name)


# 生产者线程
class Producer(threading.Thread):
    def run(self):
        while True:
            # 阻塞等待获得condition对象
            with condition:
                # 此处已经获取到condition对象(condition.acquire())
                print("生产者拿到condition")

                p = Product(random.randint(100, 999))
                p2 = Product(random.randint(100, 999))
                plist.append(p)
                plist.append(p2)
                print("生产者生产了", p)
                print("生产者生产了", p2, "\n")
                time.sleep(1)

                # condition.notify()  # 通知消费者,谁wait()了就通知谁
                condition.notify_all()
                condition.wait()  # 监听消费者通知,谁wait代表谁希望被notify(wait中会释放condition)

                # with走完,交出condition
                # 此处condition已释放(condition.release())


# 消费者线程
class Consumer(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        while True:

            # 阻塞等待获得condition对象(本例中一直等到生产者with condition内的代码走完)
            with condition:
                # 此处已经获取到condition对象(condition.acquire())
                print("消费者%s拿到conditon" % (self.name))
                try:
                    p = plist.pop()
                    print("%s消费者消费了" % (self.name), p)

                    print("fuck")
                except IndexError:
                    print("消费者:冰箱里空空如也")

                condition.notify()  # 通知生产者生产,谁with了相同condition且wait就通知谁
                condition.wait()  # 等候生产者消息(wait中会释放condition)
                # 此处condition已释放(condition.release())


if __name__ == '__main__':
    Producer().start()
    Consumer("foo").start()
    Consumer("bas").start()
    pass

semaphore调度线程

'''
使用Semaphore调度线程:控制最大并发量
'''
import threading

import time

# 允许最大并发量3
sem = threading.Semaphore(3)


def doSth(arg):
    with sem:
        tname = threading.current_thread().getName()
        print("%s正在执行【%s】" % (tname, arg))
        time.sleep(1)
        print("-----%s执行完毕!-----\n" % (tname))
        time.sleep(0.1)


if __name__ == '__main__':

    # 开启10条线程
    for i in range(10):
        threading.Thread(target=doSth, args=("巡山",), name="小分队%d" % (i)).start()
    pass

barrier调度线程

'''
使用Barrier调度线程:足三而行
'''
import threading
import time


def sayGoodbye():
    print("我们先撤尔等殿后")


# 3=足三而行
# action=放行时回调
# timeout=超时时间(等待超时会抛出BrokenBarrierError异常)
bar = threading.Barrier(3, action=sayGoodbye, timeout=5)


def doSth(arg):
    # 阻塞等待足三而行
    # 等待超时会抛出BrokenBarrierError异常
    tname = threading.current_thread().getName()
    try:
        bar.wait()
    except threading.BrokenBarrierError:
        print("栅栏已被捣毁,呼叫总部支援!")
        print("%s:妈的智障,劳资还要回家学拍森呢!" % (tname))
        pass

    print("%s正在执行【%s】" % (tname, arg))
    time.sleep(1)
    print("-----%s执行完毕!-----\n" % (tname))
    time.sleep(0.1)


if __name__ == '__main__':

    # 开启10条线程
    for i in range(10):
        threading.Thread(target=doSth, args=("巡山",), name="小分队%d" % (i)).start()
    pass

timer延时线程

'''
Timer延时线程
'''
import threading
import time


def doSth(arg):
    tname = threading.current_thread().getName()
    print("%s正在执行【%s】" % (tname, arg))
    time.sleep(1)
    print("-----%s执行完毕!-----\n" % (tname))


if __name__ == '__main__':
    # 延时5秒执行doSth("巡山")
    threading.Timer(5, doSth, args=["巡山"]).start()
    pass

线程池

'''
使用线程池
'''
import random
import threading

import threadpool
import time

# =================================================================
# 参数皆为单个元素,用于执行只有一个参数的并发函数请求
argsList1 = [
    "张三丰", "赵四", "王五", "六爷", "洪七公", "朱重八", "马英九"
]


# 该函数只有一个参数,参数列表使用argsList1格式
def kill(who):
    tname = threading.current_thread().getName()
    # print(threading.current_thread().isDaemon())

    print("%s开始杀%s..." % (tname, who))
    time.sleep(random.randint(1, 5))
    print("-----%s杀死了%s-----" % (tname, who))
    pass


# =================================================================




# =================================================================
# 当请求函数包含1个以上的参数时,必须使用如下格式:
# [ ([位置参数列表],{关键字参数字典}),...    ]
argsList2 = [
    ([1, "今天"], {"who": "张三丰", "reason": "已经很老了"}),
    ([2, "明天"], {"who": "赵四", "reason": "我是刘能的朋友"}),
    ([3, "后天"], {"who": "王五", "reason": "隔壁老王嫌疑人"}),
    ([4, "今天"], {"who": "六爷"}),
    ([5, "明天"], {"reason": "我是刘能的朋友"}),
    ([6, "后天"], None),
]


# 该函数有位置参数和关键字参数,参数列表使用argsList2格式
def killGood(order, when, who="No one", reason="凡人皆有一死"):
    tname = threading.current_thread().getName()
    # print(threading.current_thread().isDaemon())

    print("%s开始杀%s...,排名%d,时间%s,原因:%s" % (tname, who, order, when, reason))
    time.sleep(random.randint(1, 5))
    print("-----%s杀死了%s-----" % (tname, who))

    # print(5 / 0)
    # print("fuck")
    return "well done"
    pass


# =================================================================

# 请求执行结束回调
# request=已完成的请求
# result=任务的返回值
def onKillReturn(request, result):
    print("onKillReturn", request, result)


# 异常处理回调
# request=发声异常的请求
# exception=请求内发声的异常
def handleMyException(request, exception):
    print("handleMyException", request, exception)


if __name__ == '__main__':
    # 创建一个最大并发为4的线程池
    pool = threadpool.ThreadPool(4)

    # 创建一组执行kill()函数的请求,参数各异
    # requests = threadpool.makeRequests(kill, argsList1)
    # requests = threadpool.makeRequests(killGood, argsList2)

    # killGood=要并发执行的任务
    # argsList2=killGood的参数列表
    # callback=任务结束的回调函数
    # exc_callback=异常处理回调函数
    requests = threadpool.makeRequests(killGood, argsList2, callback=onKillReturn, exc_callback=handleMyException)

    # 将这组请求丢入线程池
    for req in requests:
        pool.putRequest(req)

    # 阻塞等待全部请求返回(线程池创建的并发默认为【守护线程】)
    pool.wait()
    print("over")
    pass

threadlocal

import threading
from threading import Thread
import time

'''
这个东西可以用在那些地方呢,比如下载,现在都是多线程下载了,
就像酷狗那样,可以同时下载很多首歌曲,那么就可以利用这个方法
来保存每个下载线程的数据,比如下载进度,下载速度之类的
'''
# threading.local()这个方法的特点用来保存一个全局变量,
# 但是这个全局变量只有在当前线程才能访问,
data = threading.local()
print(type(data))


def doSomething(num):
    tName = threading.current_thread().getName()
    '''
    localVal.val = name这条语句可以储存一个变量到当前线程,
    如果在另外一个线程里面再次对localVal.val进行赋值,
    那么会在另外一个线程单独创建内存空间来存储,
    也就是说在不同的线程里面赋值不会覆盖之前的值,
    因为每个线程里面都有一个单独的空间来保存这个数据,
    而且这个数据是隔离的,其他线程无法访问
    '''
    data.num = num
    for i in range(5):
        data.num += 1
        time.sleep(1)
        print(tName, "data.num=", data.num)
    pass


def doSomethingII(num):
    tName = threading.current_thread().getName()
    data.num = num
    for i in range(5):
        data.num += "1"
        time.sleep(1)
        print(tName, "data.num=", data.num)
    pass


if __name__ == '__main__':
    # threading.local的例子
    t1 = Thread(target=doSomething, args=(1,), name="foo")
    t2 = Thread(target=doSomethingII, args=("1",), name="bas")
    t1.start()
    t2.start()

    print("over")

猜你喜欢

转载自blog.csdn.net/weixin_42367527/article/details/83538470