精华内容
下载资源
问答
  • 当我们使用多线程的时候,每一个进程中只有一个GIL,那么这多个线程中谁拿到GIL,谁就可以使用cpu(ps:多个进程有多个GIl,但每个进程中只有一个GIL),所以当python用cpython作为解释器的时候,多线程就不是真正...

    GIl全局解释锁

    GIL:全局解释器锁。当我们使用多线程的时候,每一个进程中只有一个GIL锁,那么这多个线程中谁拿到GIL锁,谁就可以使用cpu(ps:多个进程有多个GIl锁,但每个进程中只有一个GIL),所以当python用cpython作为解释器的时候,多线程就不是真正意义上的多线程,属于伪并发的多线程。

    更多内容可以参考 知乎

    我们都知道,比方我有一个4核的CPU,那么这样一来,在单位时间内每个核只能跑一个线程,然后时间片轮转切换。但是Python不一样,它不管你有几个核,单位时间多个核只能跑一个线程,然后时间片轮转。看起来很不可思议?但是这就是GIL搞的鬼。任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器…

    问题1: 什么时候会释放Gil锁

    1. 遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放GIl
    2. 会有一个专门ticks进行计数 一旦ticks数值达到100这个时候释放GIl锁 线程之间开始竞争GIl锁(说明:ticks这个数值可以进行设置来延长或者缩减获得Gil锁的线程使用cpu的时间)

    问题2: 互斥锁和Gil锁的关系

    • Gil锁 :保证同一时刻只有一个线程能使用到cpu
    • 互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱
    • 首先假设只有一个进程,这个进程中有两个线程 Thread1,Thread2, 要修改共享的数据data, 并且有互斥锁:
      执行以下步骤:
    1. 多线程运行,假设Thread1获得GIL可以使用cpu,这时Thread1获得 互斥锁lock,Thread1可以改date数据(但并没有开始修改数据);
    2. Thread1线程在修改date数据前发生了 i/o操作 或者 ticks计数满100((注意就是没有运行到修改data数据),这个时候 Thread1 让出了Gil,Gil锁可以被竞争);
    3. Thread1 和 Thread2 开始竞争Gil (注意:如果Thread1是因为i/o 阻塞 让出的Gil,Thread2必定拿到Gil,如果Thread1是因为ticks计数满100让出Gil这个时候Thread1 和 Thread2 公平竞争);
    4. 假设 Thread2正好获得了GIL, 运行代码去修改共享数据date,由于Thread1有互斥锁lock,所以Thread2无法更改共享数据date,这时Thread2让出Gil锁, GIL锁再次发生竞争;
    5. 假设Thread1又抢到GIL,由于其有互斥锁Lock所以其可以继续修改共享数据data,当Thread1修改完数据释放互斥锁lock,Thread2在获得GIL与lock后才可对data进行修改

    以上描述了互斥锁和Gil锁的 一个关系。

    多进程中的GIL

    每个进程被fork或spawn时,其实都是开启了一个新的 python解释器进程,所以每个子进程都拥有一个独立的GIL锁。这样的话每个进程里面至少有一个住线程在跑,进程内的线程就实行GIL机制。这样就可以发挥多核优势。可以参考文档 python 3.7 multiprocessing

    总结:

    1. 线程锁是fine-grained(细粒度)的锁,程序员需要自行加/解锁来保证线程安全;

    2. 全局解释锁是coarse-grained(粗粒度)的锁,语言层面本身维护着一个全局的锁机制用来保证线程安全;

    3. 前一种方式比较典型的是 Java, Jython 等, 后一种方式比较典型的是 CPython (即Python)。

    展开全文
  • 主要介绍了Python全局锁中如何合理运用多线程(多进程),需要的朋友可以参考下
  • python 全局锁

    2018-12-10 16:45:50
    python全局锁问题 问题 你已经听说过全局解释器锁GIL,担心它会影响到多线程程序的执行性能。 解决方案 尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,...

    python的全局锁问题

    问题

    你已经听说过全局解释器锁GIL,担心它会影响到多线程程序的执行性能。

    解决方案

    尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。 GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。

    在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。

    而对于依赖CPU的程序,你需要弄清楚执行的计算的特点。 例如,优化底层算法要比使用多线程运行快得多。 类似的,由于Python是解释执行的,如果你将那些性能瓶颈代码移到一个C语言扩展模块中, 速度也会提升的很快。如果你要操作数组,那么使用NumPy这样的扩展会非常的高效。 最后,你还可以考虑下其他可选实现方案,比如PyPy,它通过一个JIT编译器来优化执行效率 。

    还有一点要注意的是,线程不是专门用来优化性能的。 一个CPU依赖型程序可能会使用线程来管理一个图形用户界面、一个网络连接或其他服务。 这时候,GIL会产生一些问题,因为如果一个线程长期持有GIL的话会导致其他非CPU型线程一直等待。 事实上,一个写的不好的C语言扩展会导致这个问题更加严重, 尽管代码的计算部分会比之前运行的更快些。

    1. 利用进程池

    说了这么多,现在想说的是我们有两种策略来解决GIL的缺点。 首先,如果你完全工作于Python环境中,你可以使用 multiprocessing 模块来创建一个进程池, 并像协同处理器一样的使用它。例如,假如你有如下的线程代码:

    # Processing pool (see below for initiazation)
    pool = None
    
    # Performs a large calculation (CPU bound)
    def some_work(args):
        ...
        return result
    
    # A thread that calls the above function
    def some_thread():
        while True:
            ...
            r = pool.apply(some_work, (args))
            ...
    
    # Initiaze the pool
    if __name__ == '__main__':
        import multiprocessing
        pool = multiprocessing.Pool()
    

    2. 使用C扩展编程技术

    主要思想是将计算密集型任务转移给C,跟Python独立,在工作的时候在C代码中释放GIL。 这可以通过在C代码中插入下面这样的特殊宏来完成:

    #include "Python.h"
    ...
    
    PyObject *pyfunc(PyObject *self, PyObject *args) {
       ...
       Py_BEGIN_ALLOW_THREADS
       // Threaded C code
       ...
       Py_END_ALLOW_THREADS
       ...
    }
    

    如果你使用其他工具访问C语言,比如对于Cython的ctypes库,你不需要做任何事。 例如,ctypes在调用C时会自动释放GIL。

    这些解决GIL的方案并不能适用于所有问题.

    展开全文
  • GIL(Global Interpreter Lock,即全局解释器),Python实质上并不存在真正的多线程,只有一个主线程在调度,由于GIL采用轮流运行线程的机制,GIL需要在线程之间不断轮流进行切换,线程如果较

    Python中如果是 I/O密集型的操作,用多线程(协程Asyncio、线程Threading),如果I/O操作很慢,需要很多任务/线程协同操作,用Asyncio,如果需要有限数量的任务/线程,那么使用多线程。
    如果是CPU密集型操作,用多进程(multeprocessing)。

    一、GIL

    GIL(Global Interpreter Lock,即全局解释器锁),Python实质上并不存在真正的多线程,只有一个主线程在调度,由于GIL采用轮流运行线程的机制,GIL需要在线程之间不断轮流进行切换,线程如果较多或运行时间较长,切换带来的性能损失可能会超过单线程。

    二、CPython引进GIL的主要原因是:

    1. 设计者为了规避类似内存管理这样的复杂竞争风险问题(race condition);
    2. CPython大量使用C语言库,但大部分C语言库都不是线程安全的(线程安全会降低性能和增加复杂度)。

    三、绕过GIL的两种思路:

    1. 绕过CPython,使用JPython等别的实现;
    2. 把关键性能代码放到其他语言中实现,比如C++

    四、线程、进程、协程

    • 线程、进程——把内部资源、任务调度交给底层的CPU后者内核来处理。
    • 协程 类似线程,资源、任务的调度更灵活,完全由人工处理,对开发人员及使用的人来说要求更高。其切换资源等要比线程更高效。

    五、协程、线程、进程代码示例

    asyncio包含有协程需要的所有魔法
    Asyncio 和其他 Python 程序一样,是单线程的,它只有一个主线程event loop,但是可以进行多个不同的任务(task),这里的任务,就是特殊的 future 对象。这些不同的任务,被一个叫做 event loop 的对象所控制。

    当在 jupyter 中运行: %time asyncio.run(main([‘url_1’, ‘url_2’, ‘url_3’,
    ‘url_4’]))
    出现报错: RuntimeError: asyncio.run() cannot be called from a running event loop 原因是: The problem in your case is that jupyter (IPython)
    is already running an event loop (for IPython ≥ 7.0) 解决是: 将 %time
    asyncio.run(main([‘url_1’, ‘url_2’, ‘url_3’, ‘url_4’])) 换成 await
    main([‘url_1’, ‘url_2’, ‘url_3’, ‘url_4’])

    1. 协程

    # 协程 
    
    import asyncio
    
    async def crawl_page(url):
        print('crawling {}'.format(url))
        sleep_time = int(url.split('_')[-1])
        await asyncio.sleep(sleep_time)
        print('OK {}'.format(url))
    
    async def main(urls):
        tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
        for task in tasks:
            await task
    
    # %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
    await main(['url_1', 'url_2', 'url_3', 'url_4'])
    

    结果如下图:
    在这里插入图片描述

    2.线程

    threading
    threadingpool

    # 异步编程
    
    from threading import Thread
    import time
    import functools
    
    # 装饰器实现耗时记录
    def log_execution_time(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            res = func(*args, **kwargs)
            end = time.perf_counter()
            print('{} took {} s'.format(func.__name__, (end - start)))
            return res
        return wrapper
        
    @log_execution_time
    def CountDown(n):
        while n > 0:
            n -= 1
    
    n = 100000000
    
    t1 = Thread(target=CountDown, args=[n // 2])
    t2 = Thread(target=CountDown, args=[n // 2])
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    

    等待运行结束并返回结果

    import concurrent.futures
    import requests
    import time
            
    def download_one(url):
        resp = requests.get(url)
        # print('Read {} from {}'.format(len(resp.content), url))
        return('Read {} from {}'.format(len(resp.content), url))
    
    
    def download_all(sites):
        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
            # executor.map(download_one,sites)
            to_do = []
            for site in sites:
                future = executor.submit(download_one, site)
                to_do.append(future)
    
            for future in to_do:
                future.add_done_callback(lambda future: print(future.result()))
    
    
    def main():
        sites = [
                 'https://news.qq.com/',
                 'http://www.ifeng.com/',
                 'http://www.ce.cn/',
                 'https://news.baidu.com/',
                 'http://www.people.com.cn/',
                 'http://www.ce.cn/',
                 'https://news.163.com/',
                 'http://news.sohu.com/'
        ]
        start_time = time.perf_counter()
        download_all(sites)
        end_time = time.perf_counter()
        print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
    
    if __name__ == '__main__':
        main()
    

    3. 进程

    import multiprocessing
    import time
    
    def cpu_bound(number):
        print(number)
        return sum(i * i for i in range(number))
    
    def find_sums(numbers):
        with multiprocessing.Pool() as pool:
            pool.map(cpu_bound, numbers)
    
    if __name__ == "__main__":
        numbers = [1000000 + x for x in range(20)]
    
        start_time = time.time()
        find_sums(numbers)
        duration = time.time() - start_time
        print(f"Duration {duration} seconds")
    
    展开全文
  • Python : GIL线程全局锁

    2019-06-13 09:16:01
    线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python多线程起到作用,但对于cpu密集型任务,python...

    线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,说白了就是一个核只能在同一时间运行一个线程.对于io密集型任务,python的多线程起到作用,但对于cpu密集型任务,python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。
    解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能).

    展开全文
  • Python是一门解释型的编程语言, GIL是一把全局的大 GIL是一把在底层工作的,所有的Python解释器的线程模型都逃不过。GIL在Cpython中对所有的线程编程进行线程安全管理。 进程与线程 进程与线程:进程相当于一...
  • 一、多线程开发——共享全局数据问题 多线程开发的时候共享全局变量会带来资源竞争效果,也就是数据不安全。 互斥/线程: 当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个...
  • python全局解释器GIL(c python的一个缺陷) 同一时间只能一个线程工作,防止数据出错,因为有可能一份数据同时被两个线程修改。 但是有全局解释器GIL的情况下,数据依然可能出错,GIL只是保证同一时间只有...
  • 2.多线程的优点 使用线程可以把占据长时间的程序中的任务放到后台去处理。 在处理I/O密集程序的运行速度可能加快(ps:计算型密集任务除外) 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等。在这种...
  • Python在设计之初就考虑到要在主循环中,并且同时只有一个线程来控制,对于python下的多线程也一样,因为对于Python虚拟机的访问由全局解释器(Global Interpreter Lock)来控制,正式是这个,能保证同一时刻...
  • 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。 就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。 >有名的编译器例如GCC,...
  • - 多线程的理解: 多进程和多线程都可以执行多个任务,线程是进程的一部分。...Python多线程并不能真正能发挥作用,因为在Python中,有一个GIL,即全局解释,该的存在保证在同一个时间只能有...
  • 但长期以来,Python最为人诟病的就是它有一把:GIL,这把Python无法真正的实现多线程执行,无法利用多核CPU的高性能。(但是可以多进程) GIL(Global Interpreter Lock)全局解释器 In short, Python...
  • python GIL 全局锁,多核cpu下的多线程性能究竟如何?GIL全称Global Interpreter Lock GIL是什么? 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是...
  • 因为多线程的编程方式,使得线程之间的数据的一致性和状态同步难以把控,为了解决这一数据不能同步的问题,设计了GIL全局解释器。 三、全局解释器如何发挥作用的 在Cpython解释器中,当python代码有一个进程...
  • python3-多线程锁机制

    2020-08-09 09:07:58
    线程B只能等到线程A释放lock.release()后(归还)才能运行lock.acquire()(拿到全局唯一的)并执行后面的代码 """ 使用 lock = threading.Lock() # 生成对象,全局唯一 lock.acquire() # 获取。未获取...
  • 1. GIL是什么?  首先需要明确的一点是GIL并不是python的特性, 它是在实现python解析器(Cpython)时所引入的一个概念。... 为了更有效的利用多核处理器的性能,就出现了多线程的编程方式,而随之带来...
  • GIL(全局解释器) GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念,是为了实现不同线程对共享资源访问的互斥,才引入了GIL 在Cpython解释器中,同一个进程下开启的多线程,同一时刻...
  • 当我们使用多线程的时候,每一个进程中只有一个GIL,那么这多个线程中谁拿到GIL,谁就可以使用cpu(ps:多个进程有多个Gil,但每个进程中只有一个GIL),所以当python用cpython作为解释器的时候,多线程就不是真正...
  • Python多线程005之全局解释性GIL

    千次阅读 2018-04-08 11:28:40
    # 什么是全局解释器GIL 线程锁 def run(n): # 加一个数据 lock.acquire() global num # time.sleep(2) #sleep 不占用cpu num += 1 # time.sleep(0.1) #所有正常加数据时,不要加sleep也...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,851
精华内容 740
关键字:

python全局锁多线程

python 订阅