【学习笔记】python—多任务编程(线程)

继上一篇:【学习笔记】python—多任务编程(进程)


目录

1 线程

1.1 线程基本概念

1.2  Thread's notes

1.2.1 线程之间的执行是无序的

1.2.2 主线程默认等待所有子线程执行结束再结束

1.2.3 线程之间共享全局变量

1.2.4 线程之间共享全局变量导致的错误

1.2.5 线程同步和互斥锁


1 线程

1.1 线程基本概念

线程是cpu调度的基本单位,依附于进程。同一进程的所有子线程共用同一存储空间

线程一定是并发!
    创建子线程:

1、导入线程模块

import threading

2、创建线程对象

格式:threading.Thread(group[,target[,name[,args[,kwargs]]]])

group: 目前只能是None

target: 指定执行的任务

name: 给子线程命名,默认是thread_n

args:以元组的方式给执行任务传参(one,)

kwargs:以字典的方式给任务传参

  3.启动线程

 线程名.start()

import threading
import time


def work_one(count):
    sum = 0
    for i in range(count):
        sum+=i
        print("work_one_sum:",sum)
        time.sleep(0.1)


def work_two():
    for i in range(5):
        print("work_two_i:",i)
        time.sleep(0.1)

if __name__=="__main__":

    sub_thread_one = threading.Thread(target=work_one, args=(3,))

    sub_thread_two = threading.Thread(target=work_two)


    sub_thread_one.start()
    sub_thread_two.start()

执行结果:

work_one_sum: 0
work_two_i: 0
work_one_sum: 1
work_two_i: 1
work_one_sum: 3
work_two_i: 2
work_two_i: 3
work_two_i: 4

Process finished with exit code 0

由结果可以得出:线程间的执行是无序

1.2  Thread's notes

1.2.1 线程之间的执行是无序的

(代码演示略)

因为线程的执行是由cpu调度的,绝大多数情况下,写在代码前面的先被执行。(先得到cup的调度),如果前面的线程赶上cpu忙则可能被后面的线程先搭上cpu这趟列车。

1.2.2 主线程默认等待所有子线程执行结束再结束

import threading
import time


def work_one(count):
    sum = 0
    for i in range(count):
        sum+=i
        print("work_one_sum:",sum)
        time.sleep(0.1)


def work_two():
    for i in range(5):
        print("work_two_i:",i)
        time.sleep(0.1)

if __name__=="__main__":

    sub_thread_one = threading.Thread(target=work_one, args=(3,))

    sub_thread_two = threading.Thread(target=work_two)


    sub_thread_one.start()
    sub_thread_two.start()


    # 主线程延时0.2s
    time.sleep(0.2)
    print("代码执行到此处,主线程执行完毕!")

执行结果:

work_one_sum: 0
work_two_i: 0
work_one_sum: 1
work_two_i: 1
代码执行到此处,主线程执行完毕!
work_one_sum: 3
work_two_i: 2
work_two_i: 3
work_two_i: 4

Process finished with exit code 0

结果显示:主线程执行完并没有立即退出程序,而是在等待子线程结束。

若想主线程执行完毕立即退出程序 ,可设置守护主线程。

守护主线程的方式:

1、threading.Tread(target= 任务名,daemon=True)

2、线程对象.setDaemon(True)

def work_one(count):
    sum = 0
    for i in range(count):
        sum+=i
        print("work_one_sum:",sum)
        time.sleep(0.1)


if __name__=="__main__":

    sub_thread_one = threading.Thread(target=work_one, args=(3,),daemon=True)
    #sub_thread_one.setDaemon(True)

    sub_thread_one.start()

    # 主线程延时0.2s
    time.sleep(0.2)
    print("代码执行到此处,主线程执行完毕!")

执行结果:

work_one_sum: 0
work_one_sum: 1
代码执行到此处,主线程执行完毕!

Process finished with exit code 0

1.2.3 线程之间共享全局变量

由于多线程之间共享一块存储空间,所以线程之间能共享全局变量。

import threading
import time


# 定义全局变量
num = 0


def op_one():
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op1_num",num)

def op_two():
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op2_num",num)

if __name__=="__main__":

    sub_thread1 = threading.Thread(target=op_one)
    sub_thread2 = threading.Thread(target=op_two)


    # 启动线程
    sub_thread1.start()
    sub_thread2.start()

执行结果:

op1_num 1
op1_num 2
op1_num 3
op1_num 4

........

op1_num 97
op1_num 98
op1_num 99
op1_num 100
op2_num 101
op2_num 102
......


op2_num 198
op2_num 199
op2_num 200

由结果可能看出:线程间可共享全局变量。

注意:此代码运行会出错,这里的结果不出错,只是巧合。

1.2.4 线程之间共享全局变量导致的错误

将1.2.3的代码多运行几次结果如下:

/usr/bin/python3.5 /home/python/untitled/1.py
op1_num 1
op1_num 2
op1_num 3
.......
.......
op2_num 181
op2_num 181
op2_num 182
op2_num 183
op2_num 184
op2_num 185
........
........
op2_num 195
op2_num 196
op2_num 197
op2_num 198

Process finished with exit code 0

多线程同时对全局变量操作数据发生了错误

错误分析:

两个线程sub_thread1和sub_thread2都要对全局变量num(默认是0)进行加1运算,但是由于是多线程同时操作,有可能出现下面情况:

  1. 在num=0时,sub_thread1取得num=0。此时系统把sub_thread1调度为”sleeping”状态,把sub_thread32转换为”running”状态,t2也获得num=0
  2. 然后sub_thread2对得到的值进行加1并赋给num,使得num=1
  3. 然后系统又把sub_thread1调度为”sleeping”,把first_thread转为”running”。线程t1又把它之前得到的0加1后赋值给num。
  4. 这样导致虽然first_thread和first_thread都对g_num加1,但结果仍然是num=1

1.2.5 线程同步和互斥锁

解决错误的方法:

1、线程同步  线程等待(join)

  线程同步:一个任务执行完成以后另一个任务才能执行,同一个时刻只有一个任务在执行

特点是:顺序执行

import threading
import time


# 定义全局变量
num = 0


def op_one():
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op1_num",num)


def op_two():
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op2_num",num)


if __name__=="__main__":

    sub_thread1 = threading.Thread(target=op_one)
    sub_thread2 = threading.Thread(target=op_two)


    # 启动线程
    sub_thread1.start()
    # 线程同步:一个任务执行完成以后另一个任务才能执行,同一个时刻只有age任务在执行

    sub_thread1.join()

    sub_thread2.start()

执行结果:先完成义务一,再执行任务二。

/usr/bin/python3.5 /home/python/untitled/1.py
op1_num 1
op1_num 2
op1_num 3
op1_num 4
......
......
......


op1_num 100
op2_num 101
op2_num 102
......
......
......


op2_num 199
op2_num 200

Process finished with exit code 0

2.互斥锁

互斥锁:对共享的数据进行锁定,保证同一时刻知有一个线程对共享数据进行操作。

不同线程之间,谁先抢到互斥锁,谁就先执行,用完释放后,不同的线程再去抢占该锁。

步骤:

1、创建锁

lock = threading.Lock

2、上锁

lock.acquire()

3、释放锁

lock.release()

acquire和release方法之间的代码同一时刻只能有一个线程去操作

如果在调用acquire方法的时候 其他线程已经使用了这个互斥锁,那么此时acquire方法会堵塞,直到这个互斥锁释放后才能再次上锁。

import threading
import time


# 定义全局变量
num = 0
lock = threading.Lock()

def op_one():
    # 上锁
    lock.acquire()
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op1_num",num)
    # 释放锁
    lock.release()


def op_two():
    # 上锁
    lock.acquire()
    for i in range(100):
        global num  # 地址引用
        num +=1
        print("op2_num",num)
    # 释放锁
    lock.release()

if __name__=="__main__":

    sub_thread1 = threading.Thread(target=op_one)
    sub_thread2 = threading.Thread(target=op_two)


    # 启动线程
    sub_thread1.start()
    # 线程同步:一个任务执行完成以后另一个任务才能执行,同一个时刻只有age任务在执行

    sub_thread1.join()

    sub_thread2.start()

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值