Python之线程(10)

定义:

线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

为什么要使用多线程?

1、线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄和其他进程应有的状态。    

2、因为线程的划分尺度小于进程,使得多线程程序的并发性高。

3、进程在执行过程之中拥有独立的内存单元,而多个线程共享内存,从而极大的提升了程序的运行效率。

4、线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享一个进程的虚拟空间。

5、线程的共享环境:包括进程代码段、进程的共有数据等,利用这些共享的数据,线程之间很容易实现通信。

6、操作系统在创建进程时,必须为该进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程来实现并发比使用多进程的性能高得要多。

正确使用线程:

进程之间不能共享内存,但线程之间共享内存非常容易。

操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高。

 python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。

可以理解为CPU分配的进程里面分配几个线程,线程可以完成一些耗时操作(比如下载操作),线程被称作轻量级线程LWP(Lightweight Process);

进程:计算密集型操作;

死锁:

from threading import Thread,Lock
import time

lockA = Lock()
lockB = Lock()

class MyThread1(Thread):
    def run(self):
        if lockA.acquire():
            print(self.name+"获取了A锁")
            time.sleep(0.1)
            if lockB.acquire():
                print(self.name+"获取了B锁")
                lockB.release()
            lockA.release()

class MyThread2(Thread):
    def run(self):
        if lockB.acquire():
            print(self.name+"获取了B锁")
            time.sleep(0.1)
            if lockA.acquire():
                print(self.name+"获取了A锁")
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()

    t1.start()
    t2.start()

解决办法:

(1)timeout

from threading import Thread,Lock
import time

lockA = Lock()
lockB = Lock()

class MyThread1(Thread):
    def run(self): #start()
        if lockA.acquire():
            print(self.name + "获取了A锁")
            time.sleep(0.1)
            if lockB.acquire():
                print(self.name + "获取了B锁")
                lockB.release()
            lockA.release()

class MyThread2(Thread):
    def run(self):
        if lockB.acquire():
            print(self.name + "获取了B锁")
            time.sleep(0.1)
            if lockA.acquire(timeout=5):
                print(self.name + "获取了A锁")
                lockA.release()
            lockB.release()


if __name__ == '__main__':
    t1 = MyThread1()
    t2 = MyThread2()

    t1.start()
    t2.start()

(2)重构代码

生产者与消费者

#线程之间的通信
#生产者与消费者
#通过队列
import threading
import queue
import random
import time

def produce(q):
    i = 0
    while i < 10:
        num = random.randint(1,100)
        q.put("生产者产生数据:%d" % num)
        print("生产者产生数据:%d" % num)
        time.sleep(1)
        i += 1
    q.put(none)
    #完成任务
    q.task_done()

def consume(q):
    while true:
        item = q.get()
        if item is none:
            break
        print("消费者获得:%s" % item)
        time.sleep(4)
    #完成任务
    q.task_done()


if __name__ == "__main__":
    q = queue.queue(10)
    arr = []

    #创建生产者
    th = threading.thread(target=produce,args=(q,))
    th.start()

    #创建消费者
    tc = threading.thread(target=consume,args=(q,))
    tc.start()

    th.join()
    tc.join()

    print("end")

'''
1、GIL  全局解释器
在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少个核
同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。
GIL的全程是全局解释器,来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以
把GIL看做是“通行证”,并且在一个python进程之中,GIL只有一个。拿不到线程的通行证,并且在一个python进程中,GIL只有一个,
拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操
作cpu,而只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的
python在使用多线程的时候,调用的是c语言的原生过程。

2、python针对不同类型的代码执行效率也是不同的
1、CPU密集型代码(各种循环处理、计算等),在这种情况下,由于计算工作多,ticks技术很快就会达到阀值,然后出发GIL的
释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。
2、IO密集型代码(文件处理、网络爬虫等设计文件读写操作),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,
造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序的执行
效率)。所以python的多线程对IO密集型代码比较友好。

主要要看任务的类型,我们把任务分为I/O密集型和计算密集型,而多线程在切换中又分为I/O切换和时间切换。如果任务属于是I/O密集型,
若不采用多线程,我们在进行I/O操作时,势必要等待前面一个I/O任务完成后面的I/O任务才能进行,在这个等待的过程中,CPU处于等待
状态,这时如果采用多线程的话,刚好可以切换到进行另一个I/O任务。这样就刚好可以充分利用CPU避免CPU处于闲置状态,提高效率。但是
如果多线程任务都是计算型,CPU会一直在进行工作,直到一定的时间后采取多线程时间切换的方式进行切换线程,此时CPU一直处于工作状态,
此种情况下并不能提高性能,相反在切换多线程任务时,可能还会造成时间和资源的浪费,导致效能下降。这就是造成上面两种多线程结果不同的解释。
3、结论:
I/O密集型任务,建议采取多线程,还可以采用多进程+协程的方式(例如:爬虫多采用多线程处理爬取的数据);对于计算密集型任务,python此时就不适用了。

摘录自:https://blog.csdn.net/weixin_40481076/article/details/101594705
'''

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值