我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python的线程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python的线程

本文是基于Py2.X

线程

多任务可以由多进程完成,也可以由一个进程内的多线程完成。

我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。

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

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

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

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:

# -*- coding:utf-8 -*-
import time, threading

# 新线程执行的代码:
def loop():
    print 'thread %s is running...' % threading.current_thread().name
    n = 0
    while n < 5:
        n = n + 1
        print 'thread %s >>> %s' % (threading.current_thread().name, n)
        time.sleep(1)
    print 'thread %s ended.' % threading.current_thread().name

print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name

得到:
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.

由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……

Lock

多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

import time, threading

# 假定这是你的银行存款:
balance = 0

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        change_it(n)

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print balance

得到:
46,
且每次运行结果都会不一样。

我们定义了一个共享变量balance,初始值为0,并且启动两个线程,先存后取,理论上结果应该为0,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执行时,只要循环次数足够多,balance的结果就不一定是0了。

由于彼此间的交替运算,所以结果会发生变化,如果是在银行操作,一存一取就可能导致余额不对,所以必须确保一个线程在修改balance的时候,别的线程一定不能改。

如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

修改后的代码:

# -*- coding:utf-8 -*-
import time, threading

# 假定这是你的银行存款:
balance = 0

def change_it(n):
    # 先存后取,结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n

lock = threading.Lock()

def run_thread(n):
    for i in range(100000):
        # 先要获取锁:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要释放锁:
            lock.release()

t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print balance

结果,无论怎么执行都是0,这正是我们期望的结果。

当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。

获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。

锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

全局解释器

如果你不幸拥有一个多核CPU,你肯定在想,多核应该可以同时执行多个线程。

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

ThreadLocal

在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。

但是局部变量也有问题,就是在函数调用的时候,传递起来很麻烦:

def process_student(name):
    std = Student(name)
    # std是局部变量,但是每个函数都要用它,因此必须传进去:
    do_task_1(std)
    do_task_2(std)

def do_task_1(std):
    do_subtask_1(std)
    do_subtask_2(std)

def do_task_2(std):
    do_subtask_2(std)
    do_subtask_2(std)

每个函数一层一层调用都这么传参数那还得了?用全局变量?也不行,因为每个线程处理不同的Student对象,不能共享。

如果用一个全局dict存放所有的Student对象,然后以thread自身作为key获得线程对应的Student对象如何?

global_dict = {}

def std_thread(name):
    std = Student(name)
    # 把std放到全局变量global_dict中:
    global_dict[threading.current_thread()] = std
    do_task_1()
    do_task_2()

def do_task_1():
    # 不传入std,而是根据当前线程查找:
    std = global_dict[threading.current_thread()]
    ...

def do_task_2():
    # 任何函数都可以查找出当前线程的std变量:
    std = global_dict[threading.current_thread()]
    ...

这种方式理论上是可行的,它最大的优点是消除了std对象在每层函数中的传递问题,但是,每个函数获取std的代码有点丑。

有没有更简单的方式?

ThreadLocal应运而生,不用查找dict,ThreadLocal帮你自动做这件事:

import threading

# 创建全局ThreadLocal对象:
local_school = threading.local()

def process_student():
    print 'Hello, %s (in %s)' % (local_school.student, threading.current_thread().name)

def process_thread(name):
    # 绑定ThreadLocal的student:
    local_school.student = name
    process_student()

t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()

得到:

Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)

全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。

可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python的线程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Python的线程

本文是基于Py2.X线程多任务可以由多进程完成,也可以由一个进程内的多线程完成。我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程。多线程类似于同时执行多个不同程序,多线程运行有如下优点:可以把运行时间长的任务放到后台去处理。用户
2023-01-31

python中的线程

线程的理解应该结合进程来对比理解更直接如果我们操作系统当做一个工厂的话,那么创建一个进程就相当于在这个工厂里面新增了一个车间,车间里面存放了很多资源,而车间要运行起来很显然的标志就是流水线,而这些流水线就是线程,可以说线程是执行代码的最小单
2023-01-31

Python 线程

线程线程指的就是代码的执行过程进程其实是一个资源单位,而进程内的线程才是CPU上的执行单位在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过
2023-01-30

Python多线程编程,线程锁

多线程threading 模块创建线程创建自己的线程类线程通信线程同步互斥方法线程锁@需要了解!!! 什么是线程?线程也是一种多任务的编程方法,可以利用计算机多核资源完成程序的并发运行。线程又被称为轻量级进程线程的特征线程是计算机多核分配的
2023-01-30

【Python】python获取线程的返

threading模块默认没有获取线程返回值的方法。下面通过重写threading.Tread类来实现:from threading import Threadimport randomimport timeclass MyThread(T
2023-01-31

Python学习—python中的线程

1.线程定义线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个进程至少有一个线程,一个进程必定有一个主
2023-01-31

python多线程线程锁的使用方法

小编给大家分享一下python多线程线程锁的使用方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!python的数据类型有哪些?python的数据类型:1. 数字
2023-06-14

python线程、协程

线程Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。更多方法:start            线程准备就绪,等待CPU调度setName      为线程设置名称getName      获取线程名称setDae
2023-01-31

Python线程的编程方式

这篇文章主要讲解了“Python线程的编程方式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python线程的编程方式”吧!1、调用thread模块中的start_new_thread()函
2023-06-17

python线程编程

1、线程模式代码#!/usr/bin/python#_*_coding:utf-8_*_import threadingimport timedef Producer():    print 'chef : 等人来买包子。。。'    ev
2023-01-31

Python进程、线程

1.线程启动def run(n): print('%s in thread...'%n)t=threading.Thread(target=run,args=(n,))t.start#线程等待t.join2.多线程同时启动并等待执行结
2023-01-31

python—多线程

一、多线程实例  线程时应用程序中工作的最小单位,python中提供了threading模块来对多线程操作,一般多核cpu采用多进程方式,单核才采用多线程方式  方法:  将要执行的方法threading.Thread作为参数传给构造方法(
2023-01-31

python多线程

Python 多线程多线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度程序
2023-01-30

python多线程的线程如何安全实现

1、引言 当前随着计算机硬件的快速发展,个人电脑上的 CPU 也是多核的,现在普遍的 CUP 核数都是 4 核或者 8 核的。因此,在编写程序时,需要为了提高效率,充分发挥硬件的能力,则需要编写并行的程序。Java 语言作为互联网应用的主要
2022-06-02

如何进行Python线程的多线程展示

如何进行Python线程的多线程展示,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。什么多线程?多线程,就是多个独立的运行单位,同时执行同样的事情。想想一下,文章发布后同时被
2023-06-22

Python 多线程

文章来源:https://www.runoob.com/python/python-multithreading.html多线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理。用户界面
2023-01-31

Python线程锁

多线程的优势:可以同时运行多个任务但是当多个线程同时访问共享数据时,可能导致数据不同步,甚至错误!so,不使用线程锁, 可能导致错误购买车票--线程锁[root@~]# cat test.py #-*- coding:utf-8 -*-im
2023-01-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录