前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python线程入门

python线程入门

作者头像
py3study
发布2020-01-17 10:56:05
6770
发布2020-01-17 10:56:05
举报
文章被收录于专栏:python3

目录

python线程入门

正常情况下,我们在启动一个程序的时候。这个程序会先启动一个进程,启动之后这个进程会启动起来一个线程。这个线程再去处理事务。也就是说真正干活的是线程,进程这玩意只负责向系统要内存,要资源但是进程自己是不干活的。默认情况下只有一个进程只会拉起来一个线程。

多线程顾名思义,就是同样在一个进程的情况同时拉起来多个线程。真正干活的是线程。进程与线程的关系就像是工厂和工人的关系, 要想一个工厂运行起来,至少有一个工,当然如果工人多, 那么效率就变高了。因为只有一个进程,所以多线程在提高效率的同时,并没有向系统伸手要更多的内存资源。因此使用起来性价比还是很高的。但是虽然多线程不会消耗更多的内存,但是每个线程却需要CPU的的参与。

可以这样理解: 工厂虽然是固定的大小,可以容纳很多工人取干活, 但是工人干活儿需要人来协调, 如果工人太多, 对于一个固定的厂, CPU相当于厂长, 厂长的精力也是有限的, 当厂长(CPU忙不过不过来的时候效率也一样会有影响. 所以工人(线程)的数量最好还是在厂长(cpu)的能力(内核数)范围之内比较好。

线程与进程

  • 硬件发展:
    • cpu 切片,由之前的串行处理,到后来实行分片,同时执行一颗线程,处理效率更快.多核CPU 同一时刻可以执行多个线程。
  • 软件发展:
    1. 单进程单线程
    2. 多线程单进程的程序,
      • 同一个进程下的多个线程可能在多颗CPU上执行,一个线程不可能同时在多个cpu上。(其他语言会出现)
      • Python语言不会出现同一个进程下的多线程同时出现在多个CPU上,全局解释器锁GRL. 限定了当一个进程下的多个线程处理的时候,只能在一个CPU上执行,当一个线程被处理的时候,其他的线程只会等候;这样的缺点,处理效率很低。
    3. 多线程多进程的程序。 进程的开销通常比线程昂贵,因为线程自动共享内存地址空间和文件描述符. 意味着, 创建进程比创建线程会花费更多的资源和时间
    4. 在执行一些sleep/read/write/recv/send这些会导致阻塞的函数时,当前线程会主动放弃GIL,然后调用相应的系统API,完成后再重新申请GIL。因此,GIL也并不是导致Python的多线程完全没用,在一些IO等待的场合,Python多线程还是发挥了作用,当然如果多线程都是用于CPU密集的代码,那多线程的执行效率就明显会比单线程的低。
  • 程序, 线程与 进程关系
    • 一个程序可能有多个多个进程, 某一个进程里面可以包含多个线程.

线程

创建线程
  • 如何实现: 使用threading 模块
  • 创建一个简单的线程
  • 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行 启动一个线程
代码语言:javascript
复制
#!/usr/bin/env python
#-*-coding:utf-8-*-
import time, threading


# 线程执行的代码:
def thread_demo():
    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)


if __name__ == "__main__":
    print('thread %s is running...' % threading.current_thread().name)
    t = threading.Thread(target=thread_demo, name= "subThread")
    t.start()
    t.join()
    print('thread %s ended.' % threading.current_thread().name) 
代码语言:javascript
复制
#!/usr/bin/env python
#-*-coding:utf-8-*-

import threading
import time

def foo(num):
    for i in range(num):
        time.sleep(0.5)
        print i

if __name__ == "__main__":
    for x in range(3):                              # 启动多个线程
        t=threading.Thread(target=foo,args=(3,))
        t.setDaemon(False)
        t.start()
        # 通过join方法让线程逐条执行
        # t.join()

#output
0
00
11
1
1
2
22
3
3
3 
线程之Join方法正确姿势
  • 先看代码
代码语言:javascript
复制
#!/usr/bin/env python
#-*-coding:utf-8-*-

import threading
import time

def foo(num):
    for i in range(num):
        time.sleep(0.5)
        print(i)

if __name__ == "__main__":
    """
    创建一个列表,用于存储要启动多线程的实例
    """
    print("MainThread is running....")
    threads = []
    for x in range(3):
        t = threading.Thread(target=foo, args=(3,))
        # 把多线程的实例追加入列表,要启动几个线程就追加几个实例
        threads.append(t)

    for thr in threads:
        # 把列表中的实例遍历出来后,调用start()方法以线程启动运行
        thr.start()

    for thr in threads:
        """
        isAlive()方法可以返回True或False,用来判断是否还有没有运行结束
        的线程。如果有的话就让主线程等待线程结束之后最后再结束。
        """
        if thr.isAlive():
            thr.join()
    print("MainThread over")
 
线程锁(Lock)
  • 多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
  • 一个混乱的例子
代码语言:javascript
复制
#!/usr/bin/env python
#-*-coding:utf-8-*-
import time
import  threading

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

def change_it(m):
   '''正常情况下: 存多少,取多少,金额应该不变'''
    global balance
    balance = balance + m
    balance = balance - m

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

if __name__ == "_mian__":        
    t1 = threading.Thread(target=run_thread, args=(3,))
    t2 = threading.Thread(target=run_thread, args=(4,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)
 
代码语言:javascript
复制
 为线程上一把锁

 #!/usr/bin/env python
#-*-coding:utf-8-*-

import time
import threading

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

lock = threading.Lock()

def change_it(m):
   '''正常情况下: 存多少,取多少,金额应该不变'''
    global balance
    balance = balance + m
    balance = balance - m

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

if __name__ == "_mian__":        
    t1 = threading.Thread(target=run_thread, args=(3,))
    t2 = threading.Thread(target=run_thread, args=(4,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)

总结

  • 如何创建一个线程?
    • 使用 threading.Thread , 参数包含 target, args, name 等
  • 如何创建多线程?
    • 使用 for 语句
  • 什么是守护线程, 什么是主线程?
    • t.setDaemon(True) 表示的是后台线程, 表示程序流程(主线程)跑完之后直接就关闭了,然后退出了,根本不管子线程是否执行完
    • 默认t.setDaemon(False), 表示前台线程,主线程执行过程中,子线程也在进行,主线程执行完毕后,等待子线程都执行完成后,程序才会停止.
  • join() 方法 使用正确姿势: 使用线程池, 谨慎使用
  • 线程锁: threading.Lock(),解决线程间共享内存,同时对一个变量进行修改时造成数据混乱, 应用有: 比如多个多个线程对数据库同一个数据进行修改

参考

python 并发执行之多线程 Python3 多进程和多线程

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/05/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • python线程入门
  • 线程与进程
    • 线程
      • 创建线程
      • 线程之Join方法正确姿势
      • 线程锁(Lock)
    • 总结
      • 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档