Python----多线程----Thread

Python学习之路,点击有全套Python笔记

进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。

线程: cpu调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在,一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

线程的运行是没有先后顺序的
子线程全部结束,主线程才会结束
Python会自动为线程指定一个名字

import threading
import time

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

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

def main():
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    t1.start()
    t2.start()
	# 查看进程数量
    print(threading.enumerate())

if __name__ == '__main__':
    main()
通过类继承实现,只需要继承threading.Thread
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方法执行线程。

global,全局变量
  • 在一个函数中,对全局变量进行修改的时候,到底是否需要使用global进行说明,要看是否对全局变量的执行指向进行了修改。

  • 如果修改了执行,让全局变量指向了一个新的地址,那么必须用global。如果仅仅是修改了指向的空间中的数据,此时不用必须使用global

  • 多线程之间,全局变量是共享的,线程之间对全局变量的随意修改可能会引起混乱,也就是线程非安全。

# 一写一读
import threading

def test1(temp):
    temp.append(33)
    print("-------in test1 temp = %s---------" %str(temp))

def test2(temp):
    print("-------in test2 temp = %s---------" %str(temp))

g_nums = [11, 22]

def main():
    # target指定这个线程去哪执行代码
    # args指定将来调用函数的时候,传递什么数据过去
    t1 = threading.Thread(target=test1, args=(g_nums,))
    t2 = threading.Thread(target=test2, args=(g_nums,))
    t1.start()
    time.sleep(1)
    t2.start()
    time.sleep(1)
    print("-------in main g_num = %s---------" %str(g_nums))

if __name__ == '__main__':
    main()
# -------in test1 temp = [11, 22, 33]---------
# -------in test2 temp = [11, 22, 33]---------
# -------in main g_num = [11, 22, 33]---------

同步

同步就是协同步调,按预定的先后次序进行运行

互斥锁

同一进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。

# 创建锁
mutex = threading.Lock()

# 锁定
mutex.acquire()

# 释放
mutex.release()
import threading

g_num = 0

def test1(num):
    global g_num
    for i in range(num):
        # 上锁,如果之前没有被上锁,那么上锁成功
        # 如果之前已经被上锁了,那么此时会堵塞在这里,直到这个锁被解开位置
        mutex.acquire()
        g_num += 1
        # 解锁
        mutex.release()
    print("-------in test1 num = %d---------" %g_num)

def test2(num):
    global g_num
    for i in range(num):
        # 上锁
        mutex.acquire()
        g_num += 1
        # 解锁
        mutex.release()
    print("-------in test2 num = %d---------" %g_num)

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

def main():
    # target指定这个线程去哪执行代码
    # args指定将来调用函数的时候,传递什么数据过去
    t1 = threading.Thread(target=test1, args=(1000000,))
    t2 = threading.Thread(target=test2, args=(1000000,))

    t1.start()
    t2.start()

    time.sleep(1)

if __name__ == '__main__':
    main()
# -------in test1 num = 1992616----------------in test2 num = 2000000---------   

上锁解锁过程:

  • 当一个线程调用锁的acquire()方法获得锁时,锁就进入“locked”状态。

  • 每次只有一个线程可以获得锁。如果此时另一个线程试图获得这个锁,该线程就会变为“blocked”状态,称为“阻塞”,直到拥有锁的线程调用锁的release()方法释放锁之后,锁进入“unlocked”状态。

  • 线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

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

死锁

若干子线程在系统资源竞争时,每个子线程都占有一部分资源,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。
如何避免死锁,使用银行家算法或添加超时时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值