当前位置:   article > 正文

Python高级编程技巧第6讲 - Python多任务 - 线程_python学习第二阶段 吕泽

python学习第二阶段 吕泽

一、线程

1.1 多任务:

有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的

1.2 多任务的理解:

在这里插入图片描述

  • 并行::真的多任务, cpu大于当前执行的任务
  • 并发::假的多任务 ,cpu小于当前执行的任务

主线程会等到子线程执行结果之后主线程 才会结束

守护线程  不会等子线程结束		 t.setDaemon(True)
等待子线程执行结束 主线程继续执行	 t.join()
  • 1
  • 2

1.3 查看线程数量

threading.enumerate()	查看当前线程的数量
	
线程运行是没有先后顺序的,但是先执行主线程再执行子线程
  • 1
  • 2
  • 3
import threading
import time


def demo1():
    for i in range(5):
        print("--test1--%d" % i)
        time.sleep(1)
        # break


def demo2():
    for i in range(10):
        print("--test2--%d" % i)
        time.sleep(1)


def main():
    t1 = threading.Thread(target=demo1)  # 子线程1
    t2 = threading.Thread(target=demo2)	 # 子线程2

    t1.start()
    # 延时
    # time.sleep(1)
    t2.start()
    # time.sleep(1)
    # 获取当前程序所有的线程
    # 线程运行是没有先后顺序的
    while True:
        print(threading.enumerate())		# 主线程
        if len(threading.enumerate()) <= 1:
            break
        time.sleep(1)

if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

1.4 验证子线程的执行与创建

  • 当调用Thread的时候,不会创建线程。
  • 当调用Thread创建出来的实例对象的start方法的时候,才会创建线程以及开始运行这个线程。
import threading
import time


def demo():
    for i in range(5):
        print('demo')


def main():
    print(threading.enumerate())
    # 不会创建线程
    t1 = threading.Thread(target=demo)

    print(threading.enumerate())

    # 创建线程  开始运行
    t1.start()

    print(threading.enumerate())


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1.5 继承Thread类创建线程

import threading
import time

class A(threading.Thread):
    # def __init__(self):
    #     super().__init__()

    def run(self):
        for i in range(5):
            print(i)

    def demo(self):
        for i in range(5):
            print(i)


if __name__ == '__main__':
    t = A()
    t.start()
    t.demo()        # 这种方式不是多线程的方式
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

1.6 多线程共享全局变量(线程间通信)

在一个函数中,对全局变量进行修改的时候,是否要加global要看是否对全局变量的指向进行了修改,
如果修改了指向,那么必须使用global,
仅仅是修改了指向的空间中的数据,此时不用必须使用global

多线程参数-args
threading.Thread(target=test, args=(num,))
  • 1
import threading
import time

num = [11, 22]


# 多线程共享全局变量的!!!
def demo1(num):
    num.append(33)
    print("demo1---%s" % str(num))


def demo2(num):
    print("demo2---%s" % str(num))


def main():
    t1 = threading.Thread(target=demo1, args=(num, ))
    t2 = threading.Thread(target=demo2, args=(num, ))
    t1.start()
    # time.sleep(1)

    t2.start()
    # time.sleep(1)

    print("main---%s" % str(num))


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

1.7 共享全局变量资源竞争

import threading

num = 0

def demo1(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo1 ---- num :%d' % num)


def demo2(nums):
    global num
    for i in range(nums):
        num += 1
    print('demo2 ---- num :%d' % num)


def main():
    t1 = threading.Thread(target=demo1, args=(10000000,))
    t2 = threading.Thread(target=demo2, args=(10000000,))

    t1.start()
    t2.start()


    print('main-----num:%d' % num)

if __name__ == '__main__':
    main()
结果:
	main-----num :129389
	demo2----num :11024590
	demo1----num :11115370
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

字节码:

import dis


def add_sum(a):
    a =+ 1


print(dis.dis(add_sum))
	# 1 load a
	# 2 load 1
	# 3 执行add
	# 4 赋值给a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1.8 互斥锁:当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制

某个线程要更改共享数据时,先将其锁定,此时资源的状态为"锁定",其他线程不能改变,只到该线程释放资源,将资源的状态变成"非锁定",其他的线程才能再次锁定该资源。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性

如果在上锁的之前已经上锁了,再上锁会阻塞,等到释放后在上锁

创建锁
mutex = threading.Lock()

锁定
mutex.acquire()

解锁
mutex.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
import threading


num = 0
# 创建吗一个互斥锁,默认是没有上锁的
mutex = threading.Lock()

def demo1(nums):
    global num
    # 加锁
    mutex.acquire()
    for i in range(nums):
        num += 1
    # 解锁
    mutex.release()
    print('demo1----num :%d' % num)


def demo2(nums):
    global num
    # 加锁
    mutex.acquire()
    for i in range(nums):
        num += 1
    # 解锁
    mutex.release()
    print('demo2----num :%d' % num)


def main():
    t1 = threading.Thread(target=demo1, args=(10000000,))
    t2 = threading.Thread(target=demo2, args=(10000000,))

    t1.start()
    t2.start()


    print('main-----num :%d' % num)

if __name__ == '__main__':
    main()
结果:
main-----num :188218
demo1----num :10000000
demo2----num :20000000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

1.8 死锁

线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。

RLock() 可重入的锁


import threading
import time

class MyThread1(threading.Thread):
    def run(self):
        # 对mutexA上锁
        mutexA.acquire()

        # mutexA上锁后,延时1秒,等待另外那个线程 把mutexB上锁
        print(self.name+'----do1---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了
        mutexB.acquire()
        print(self.name+'----do1---down----')
        mutexB.release()

        # 对mutexA解锁
        mutexA.release()

class MyThread2(threading.Thread):
    def run(self):
        # 对mutexB上锁
        mutexB.acquire()

        # mutexB上锁后,延时1秒,等待另外那个线程 把mutexA上锁
        print(self.name+'----do2---up----')
        time.sleep(1)

        # 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了
        mutexA.acquire()
        print(self.name+'----do2---down----')
        mutexA.release()

        # 对mutexB解锁
        mutexB.release()

mutexA = threading.Lock()
mutexB = threading.Lock()

if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()
    t1.start()
    t2.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

避免死锁

  • 程序设计时要尽量避免
  • 添加超时时间等

1.9 线程同步

'''
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
'''
import threading	


class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name='小爱同学')
        # self.lock = lock
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print('{}:在'.format(self.name))
            self.cond.notify()

            self.cond.wait()
            print('{}:你猜呀'.format(self.name))
            self.cond.notify()

        # self.lock.acquire()
        # print("{}:在".format(self.name))
        # self.lock.release()
        #
        # self.lock.acquire()
        # print("{}:你猜猜".format(self.name))
        # self.lock.release()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name='天猫精灵')
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.acquire()

            print('{}:小爱同学'.format(self.name))
            self.cond.notify()

            self.cond.wait()

            print('{}:现在几点了'.format(self.name))
            self.cond.notify()

            self.cond.wait()


if __name__ == '__main__':
    # mutex = threading.RLock()
    cond = threading.Condition()

    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    # 启动顺序很重要
    xiaoai.start()
    tianmao.start()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

1.10 多任务版的UDP聊天器

'''
多任务版udp聊天:
    1 创建套接字
    2 绑定本地信息
    3 获取对方IP和端口
    4 发送、接收数据
    5 创建两个线程,去执行功能
'''
import socket
import threading

def recv_msg(udp_socket):
    '''发送数据'''
    while True:
        recv_data = udp_socket.recvfrom(1024)
        print(recv_data)

def send_msg(udp_socket, dest_ip, dest_port):
    '''接收数据'''
    while True:
        send_data = input('发送的数据:')
        udp_socket.sendto(send_data.encode('gbk'), (dest_ip, dest_port))


def main():
    '''完成UDP聊天器'''
    # 1 创建套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2 绑定本地信息
    udp_socket.bind(('', 7890))

    # 3 获取对方IP和端口
    dest_ip = '192.168.1.6'   # 对方的ip
    dest_port = 8080          # 对方的端口



    t_recv = threading.Thread(target=recv_msg, args=(udp_socket,))
    t_send = threading.Thread(target=send_msg, args=(udp_socket,dest_ip, dest_port))

    t_recv.start()
    t_send.start()

if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号