侧边栏壁纸
  • 累计撰写 119 篇文章
  • 累计创建 25 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

多任务编程线程

梁来福
2022-02-10 / 0 评论 / 0 点赞 / 0 阅读 / 9795 字
温馨提示:
本文最后更新于 2024-05-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

概念

image.png

作用

多线程可以完成多任务

多线程效果图

image.png

多线程的使用

导入线程模块

import threading

线程类Thread参数说明

image.png

启动线程

start方法

代码示例

# 导入线程模块
import threading
import time


def eat():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("吃饭子线程eat:", current_thread)

    for i in range(4):
        print("吃饭中....")
        time.sleep(0.5)


def drink():
    # 获取当前线程
    current_thread = threading.current_thread()
    print("喝水子线程drink:", current_thread)

    for i in range(4):
        print("喝水中....")
        time.sleep(0.5)


if __name__ == '__main__':
    # 获取当前线程
    current_thread = threading.current_thread()
    print("主线程:", current_thread)

    # 创建子线程
    eat_thread = threading.Thread(target=eat)
    drink_thread = threading.Thread(target=drink)
    # 启动子线程
    eat_thread.start()
    drink_thread.start()

输出

主线程: <_MainThread(MainThread, started 4378697152)>
吃饭子线程eat: <Thread(Thread-1, started 123145390465024)>
吃饭中....
喝水子线程drink: <Thread(Thread-2, started 123145407254528)>
喝水中....
吃饭中....
喝水中....
吃饭中....
喝水中....
吃饭中....
喝水中....

线程执行带有参数的任务

方法1:元祖方式传参

代码示例

import threading


def info_print(name, age):
    print("name: %s age: %d" % (name, age))


if __name__ == '__main__':
    # 元祖方式传参,要保证元祖里元素顺序和函数的参数顺序一样
    sub_thread = threading.Thread(target=info_print, args=("梁来福", 1.1))
    sub_thread.start()

输出

name: 梁来福 age: 1

方法2:字典方式传参

代码示例

import threading


def info_print(name, age):
    print("name: %s age: %d" % (name, age))


if __name__ == '__main__':
    # 元祖方式传参,要保证元祖里元素顺序和函数的参数顺序一样
    # sub_thread = threading.Thread(target=info_print, args=("梁来福", 1.1))
    # sub_thread.start()

    # 字典方式传参,要保证字典里的key和函数参数名保持一致
    sub_thread = threading.Thread(target=info_print, kwargs={"age": 1, "name": "梁来福"})
    sub_thread.start()

输出

name: 梁来福 age: 1

线程的注意点

注意点1:

线程之间执行是无序的,是由操作系统调度进程来决定的

代码示例

import threading
import time


def task():
    time.sleep(2)
    print(threading.current_thread())


if __name__ == '__main__':
    for i in range(10):
        sub_thread = threading.Thread(target=task)
        sub_thread.start()

输出

<Thread(Thread-1, started 123145564463104)><Thread(Thread-7, started 123145665200128)>
<Thread(Thread-9, started 123145698779136)>
<Thread(Thread-10, started 123145715568640)>
<Thread(Thread-5, started 123145631621120)>
<Thread(Thread-3, started 123145598042112)>

<Thread(Thread-4, started 123145614831616)><Thread(Thread-6, started 123145648410624)>
<Thread(Thread-2, started 123145581252608)>

<Thread(Thread-8, started 123145681989632)>

注意点2

主线程会等待所有的子线程执行结束再结束

代码问题展示

import threading
import time


def task():
    while True:
        print("死循环中......")
        time.sleep(0.2)


if __name__ == '__main__':
    # 创建子线程
    sub_thread = threading.Thread(target=task)
    sub_thread.start()

    time.sleep(1)
    print("Over")
    exit()

输出

死循环中......
死循环中......
死循环中......
死循环中......
死循环中......
Over
死循环中......
死循环中......
死循环中......

.............................

结论

当运行代码后,程序处于死循环中,会等待子线程执行完...

解决方法:守护主线程

写法1

import threading
import time


def task():
    while True:
        print("死循环中......")
        time.sleep(0.2)


if __name__ == '__main__':
    # 创建子线程
    # daemon=True表示守护主线程,主线程退出子线程直接销毁
    sub_thread = threading.Thread(target=task, daemon=True)
    sub_thread.start()

    time.sleep(1)
    print("Over")
    # exit()

输出

死循环中......
死循环中......
死循环中......
死循环中......
死循环中......
Over

写法2

import threading
import time


def task():
    while True:
        print("死循环中......")
        time.sleep(0.2)


if __name__ == '__main__':
    # 创建子线程
    # daemon=True表示守护主线程,主线程退出子线程直接销毁
    # sub_thread = threading.Thread(target=task, daemon=True)
    sub_thread = threading.Thread(target=task)
    sub_thread.setDaemon(True)
    sub_thread.start()

    time.sleep(1)
    print("Over")
    # exit()

输出

死循环中......
死循环中......
死循环中......
死循环中......
死循环中......
Over

注意点3

线程之间共享全局变量

代码示例

import threading
import time

# 定义全局变量
g_list = []


# 添加数据的任务
def add_task():
    for i in range(3):
        # 每循环一次把数据添加到全局变量列表
        g_list.append(i)
        print("add:", i)
        time.sleep(0.2)
    # 代码执行到此说明数据添加完成
    print("添加数据完成,总数据为:", g_list)


# 读取数据的任务
def read_task():
    print("读取出来的数据:", g_list)


if __name__ == '__main__':
    # 创建子线程
    add_thread = threading.Thread(target=add_task)
    read_thread = threading.Thread(target=read_task)
  
    # 启动线程执行任务
    add_thread.start()
    # join表示当前线程(主线程)等待子线程(添加数据)执行完成后代码再继续执行
    add_thread.join()
    read_thread.start()

输出

add: 0
add: 1
add: 2
添加数据完成,总数据为: [0, 1, 2]
读取出来的数据: [0, 1, 2]

说明

该代码展示的是:创建两个子线程(一个写入数据,一个读取数据),join保证当写入数据完成后再进行读取,读取出了写入的数据,证明线程之间共享全局变量

注意点4

线程之间共享全局变量数据出现错误问题

需求

1、定义两个函数,实现分别循环1w次,每循环一次给全局变量加1

2、创建两个子线程分别执行对应的两个函数,查看计算后的结果

代码示例

import threading

g_num = 0


# 循环1w次执行的任务
def task1():
    for i in range(10000):
        # 每循环一次给全局变量加1
        global g_num
        g_num += 1 # g_num = g_num + 1

    # 代码执行至此,说明数据计算完成
    print("task1:", g_num)


def task2():
    for i in range(10000):
        # 每循环一次给全局变量加1
        global g_num
        g_num += 1  # g_num = g_num + 1

    # 代码执行至此,说明数据计算完成
    print("task2:", g_num)


if __name__ == '__main__':
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    # 启动线程执行任务
    first_thread.start()
    second_thread.start()

错误分析

image.png

解决办法

方法1:线程等待(join)

代码示例
................
if __name__ == '__main__':
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    # 启动线程执行任务
    first_thread.start()
    # 线程等待
    first_thread.join()
    second_thread.start()
输出

task1: 10000
task2: 20000

方法2:互斥锁

概念

互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其他等待的线程再去抢这个锁

如何使用

threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁

使用步骤
# 创建锁
mutex = threading.Lock()

# 上锁
mutex.acquire()

...
这里编写代码能保证同一时刻只能有一个线程去操作,对共享数据进行锁定
...

# 释放锁
mutex.release()
注意点

image.png

代码示例

import threading

# 全局变量
g_num = 0

# 创建互斥锁 Lock本质上是一个函数
lock = threading.Lock()


# 循环1w次执行的任务
def task1():
    # 上锁
    lock.acquire()
    for i in range(10000):
        # 每循环一次给全局变量加1
        global g_num
        g_num += 1 # g_num = g_num + 1

    # 代码执行至此,说明数据计算完成
    print("task1:", g_num)
    # 释放锁
    lock.release()


def task2():
    # 上锁
    lock.acquire()
    for i in range(10000):
        # 每循环一次给全局变量加1
        global g_num
        g_num += 1  # g_num = g_num + 1

    # 代码执行至此,说明数据计算完成
    print("task2:", g_num)
    # 释放锁
    lock.release()


if __name__ == '__main__':
    first_thread = threading.Thread(target=task1)
    second_thread = threading.Thread(target=task2)

    # 启动线程执行任务
    first_thread.start()
    second_thread.start()
输出

task1: 10000
task2: 20000

总结
  • 互斥锁可以保证同一时刻只有一个线程去执行代码,能够保证全局变量的数据没有问题
  • 线程等待和互斥锁都是把多任务改成单任务去执行,保证了数据的准确性,但是执行性能会下降

死锁

概念

一直等待对方释放锁的情景就是死锁

代码示例

import threading

# 创建互斥锁
lock = threading.Lock()


# 需求:多线程同时根据下标在列表中取值,要保证同一时刻只能又一个线程去取值
def get_value(index):
    # 上锁
    lock.acquire()
    my_list = [1, 11, 111, "梁来福"]
    # 判断下标越界
    if index >= len(my_list):
        print("下标越界,无法取到值:", index)
        # 取值不成功,也需要释放互斥锁,不要影响后面的线程去取值
        # 锁需要在合适的地方进行释放,防止死锁
        lock.release()
        return

    # 根据下标取值
    value = my_list[index]
    print(value)
    # 释放锁
    lock.release()


if __name__ == '__main__':
    # 创建10个线程,同时执行根据下标取值的任务
    for i in range(10):
        # 每循环一次创建一个子线程
        sub_thread = threading.Thread(target=get_value, args=(i,))
        # 启动线程执行任务
        sub_thread.start()

输出

1
11
111
梁来福
下标越界,无法取到值: 4
下标越界,无法取到值: 5
下标越界,无法取到值: 6
下标越界,无法取到值: 7
下标越界,无法取到值: 8
下标越界,无法取到值: 9

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论