欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python 多任务-线程

程序员文章站 2024-01-07 08:42:58
...

有关线程的知识点:

目录

我们先说多任务的概念:

什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方:你一边吃饭,一边看电视,一边在用手机回消息,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

并发:

  • 指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)

并行:

  • 指的是任务数小于等于cpu核数,即任务真的是一起执行的

创建第一个多线程文件

  • 使用 threading模块 创建线程 , 引用 time模块 让我们更方便观察
  • 创建Thread对象 把任务添加进去
  • 开启线程执行任务
import threading
import time

# 创建一个打饭的函数
def buyfood(num):
	# 每个数字代表一个员工
    print("员工%d打饭" % num)
    # 等待两秒(假设每个人打饭要花两秒钟)
    time.sleep(2)


if __name__ == "__main__":
	# 1创建五个人序号是0~5
    for i in range(5):
        #2创建Thread对象 把任务添加进去
        t = threading.Thread(target=buyfood, args=(i,))
        #3.开启线程执行任务
        t.start()  # 启动线程,即让线程开始执行

Thread对象的参数1:把函数名字传进去. 参数2:传入参数1那个函数所需的参数(元组格式).
现在运行一下,可以明显看出,五个人是一瞬间同时完成,然后等待了两秒程序才结束了
注意: 当调用start()时,才会真正的创建线程,并且开始执行

线程执行代码的封装:
通过使用threading模块Thread类能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,直接定义一个新的子类(class) ,只要继承threading.Thread就可以了,然后重写run方法
步骤

  1. 导包threading

  2. 写一个类继承threading.Thread

  3. 重写run方法

  4. 实例化创建的类得到对象

  5. 调用对象的start开启任务

代码:

import threading
import time

class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm "+self.name+' @ '+str(i) #name属性中保存的是当前线程的名字
            print(msg)


if __name__ == '__main__':
    t = MyThread()
    t.start()

说明:python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。

多线程-共享全局变量

from threading import Thread
import time

# 定义变量
g_num = 100

def work1():
    # 声明global 使用全局变量
    global g_num
    for i in range(3):
        g_num += 1

    print("----in work1, g_num is %d---"%g_num)


def work2():
    global g_num
    print("----in work2, g_num is %d---"%g_num)


print("---线程创建之前g_num is %d---"%g_num)

t1 = Thread(target=work1)
t1.start()

#延时一会,保证t1线程中的事情做完
time.sleep(1)

t2 = Thread(target=work2)
t2.start()

结果:
Python 多任务-线程
说明:

  • 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据
  • 缺点就是,线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)

如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确

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

同步
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。(不是一起执行)
解决线程同时修改全局变量的方式可以通过线程同步
思路:

  1. 系统调用t1,然后获取到g_num的值为0,此时上一把锁,即不允许其他线程操作g_num

  2. t1对g_num的值进行+1

  3. t1解锁,此时g_num的值为1,其他的线程就可以使用g_num了,而且是g_num的值不是0而是1

  4. 同理其他线程在对g_num进行修改时,都要先上锁,处理完后再解锁,在上锁的整个过程中不允许其他线程访问,就保证了数据的正确性

互斥锁/死锁
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁

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

threading模块中早就定义了Lock类,可以方便的处理锁定:

# 创建锁
mutex = threading.Lock()

# 锁定
mutex.acquire()

# 释放
mutex.release()

注意:
如果这个锁之前是没有上锁的,那么acquire不会堵塞 .
如果在调用acquire对这个锁上锁之前 它已经被 其他线程上了锁,那么此时acquire会堵塞,直到这个锁被解锁为止

锁的好处:

确保了某段关键代码只能由一个线程从头到尾完整地执行

锁的坏处:

阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁
死锁 :

不好解释,还是代码好理解:

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()

最后来个利用多线程的小案例: UDP聊天室

相关标签: Python知识点

上一篇:

下一篇: