多线程和GIL

多线程类似于同时执行多个不同程序,多线程运行有一下优点:

  1. 可以把运行时间长的任务放到后台处理
  2. 用户界面可以更加吸引人,比如用户点击一个按钮触发某些事件处理,可以弹出一个进度条来显示处理进度
  3. 程序运行可以更快
  4. 在一些需要等待的任务实现上,如用户输入、文件读写和网络收发数据等,线程比较有用了,在这样的情况下就可以释放一些的珍贵的资源,比如内存占用等。

Python的标准库提供了两个模块,thread和threading,thread是低级模块,threading是高级模块,对thread进行封装,大多数情况下,我们只需要使用threading这个高级模块。

1.用threading模块创建多线程

    threading模块一般通过两种方式创建多线程,第一种方式把一个函数传入并创建Thread实例,然后调用start()方法开始执行,        第二种方式直接从threading.Thread继承并创建线程类,然后重写__init__方法和run()方法  

   第一种方法:

import random
import time, threading


# 第一种方法
def thread_run(urls):
    print('Current {} is running'.format(threading.current_thread().name))
    for url in urls:
        print('{}---------------------->{}'.format(threading.current_thread().name, url))
        time.sleep(random.random())
    print('{}  end.....'.format(threading.current_thread().name))


if __name__ == '__main__':
    print('start run :{}'.format(threading.current_thread().name))
    t1 = threading.Thread(target=thread_run, args=(('url1', 'url2', 'url3'),), name='Thread1')
    t2 = threading.Thread(target=thread_run, args=(('url4', 'url5', 'url6'),), name='Thread2')
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('all {} end'.format(threading.current_thread().name))

  返回:

        

 第二种方法:

import threading, time, random


# 第二种方法
class myThread(threading.Thread):
    def __init__(self, name, urls):
        threading.Thread.__init__(self, name=name)
        self.urls = urls

    def run(self):
        print('Current {} is running'.format(threading.current_thread().name))
        for url in self.urls:
            print('{}---------------------->{}'.format(threading.current_thread().name, url))
            time.sleep(random.random())
        print('{}  end.....'.format(threading.current_thread().name))


if __name__ == '__main__':
    print('start run :{}'.format(threading.current_thread().name))
    t1 = myThread(name='Thread1', urls=['url1', 'url2', 'url3'])
    t2 = myThread(name='Thread2', urls=['url4', 'url5', 'url6'])
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('all {} end'.format(threading.current_thread().name))

    返回

   

2.线程同步

    如果多线程共同对某一个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步,使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire 和release方法,对于每次只允许一个线程操作的数据,可以将其操作到acquire 和release方法之间。

    对于Lock对象,如果一个线程连续两次进行acquire操作,那么由于第一次acquire之后没有release,第二次acquire将挂起线程,这会导致Lock对象永远不会release,使得线程死锁。  Rlock对象允许一个线程多次对acquire操作,因为在其内部通过一个counter变量维护着线程acquire的次数,而且每一次的acquire操作必须有一个release操作与之对应,在所有的release操作完成之后,别的线程才能申请该Rlock对象,例如:

# 线程同步
import threading

mylock = threading.RLock()
num = 0


class myThread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self, name=name)

    def run(self):
        global num
        while True:
            mylock.acquire()
            print('{} locked.Number:{}'.format(threading.current_thread().name, num))
            if num > 4:
                mylock.release()
                print('release  thread_name:{},num:{}'.format(threading.current_thread().name, num))
                break
            num += 1
            print('release  thread_name:{},num:{}'.format(threading.current_thread().name, num))
            mylock.release()


if __name__ == '__main__':
    t1 = myThread('thread1')
    t2 = myThread('thread2')
    t1.start()
    t2.start()

返回;

3.全局解释器锁(GIL)

  在Python的原始解释器cpython中存在着GIL(Global Interpreter Lock,全局解释器锁),因此在解释执行Python代码时,会产生互斥锁来限制线程对共享资源的访问,直到解释器遇到I/O操作或者操作次数到达一定数目时才会释放FIL。由于全局解释器锁的存在,在进行多线程操作的时候,不能调用多个CPU内核,只能利用一个内核,所以子啊进行CPU密集型操作的时候,不推荐使用多线程,更加倾向于多进程,那么多线程适合什么样的应用场景呢?对于IO密集型操作,多线程可以明显提高效率,例如Python爬虫的开发,绝大多数时间爬虫实在等待socket返回数据,网络IO的操作延时比CPU大得多。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值