Python全栈之路系列----之-----线程\threading模块介绍\GIL

线程理论

参考文档

进程与线程的一个简单解释 <http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html>

Linux进程与线程的区别<https://my.oschina.net/cnyinlinux/blog/422207>

理论链接<http://www.cnblogs.com/linhaifeng/articles/7430082.html>

 

 

What is a Thread?

 

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

 

在同一个进程内的线程的数据是可以进行互相访问的。

 

线程的切换使用过上下文来实现的,比如有一本书,有a和b这两个人(两个线程)看,a看完之后记录当前看到那一页哪一行,然后交给b看,b看完之后记录当前看到了那一页哪一行,此时a又要看了,那么a就通过上次记录的值(上下文)直接找到上次看到了哪里,然后继续往下看。

 

What is a Process?

 

一个进程至少要包含一个线程,每个进程在启动的时候就会自动的启动一个线程,进程里面的第一个线程就是主线程,每次在进程内创建的子线程都是由主线程进程创建和销毁,子线程也可以由主线程创建出来的线程创建和销毁线程。

 

进程是对各种资源管理的集合,比如要调用内存、CPU、网卡、声卡等,进程要操作上述的硬件之前都必须要创建一个线程,进程里面可以包含多个线程,QQ就是一个进程。

 

继续拿QQ来说,比如我现在打卡了QQ的聊天窗口、个人信息窗口、设置窗口等,那么每一个打开的窗口都是一个线程,他们都在执行不同的任务,比如聊天窗口这个线程可以和好友进行互动,聊天,视频等,个人信息窗口我可以查看、修改自己的资料。

 

为了进程安全起见,所以两个进程之间的数据是不能够互相访问的(默认情况下),比如自己写了一个应用程序,然后让别人运行起来,那么我的这个程序就可以访问用户启动的其他应用,我可以通过我自己的程序去访问QQ,然后拿到一些聊天记录等比较隐秘的信息,那么这个时候就不安全了,所以说进程与进程之间的数据是不可以互相访问的,而且每一个进程的内存是独立的。

 

进程与线程的区别?

 

  1. 线程是执行的指令集,进程是资源的集合
  2. 线程的启动速度要比进程的启动速度要快
  3. 两个线程的执行速度是一样的
  4. 进程与线程的运行速度是没有可比性的
  5. 线程共享创建它的进程的内存空间,进程的内存是独立的。
  6. 两个线程共享的数据都是同一份数据,两个子进程的数据不是共享的,而且数据是独立的;
  7. 同一个进程的线程之间可以直接交流,同一个主进程的多个子进程之间是不可以进行交流,如果两个进程之间需要通信,就必须要通过一个中间代理来实现;
  8. 一个新的线程很容易被创建,一个新的进程创建需要对父进程进行一次克隆
  9. 一个线程可以控制和操作同一个进程里的其他线程,线程与线程之间没有隶属关系,但是进程只能操作子进程
  10. 改变主线程,有可能会影响到其他线程的行为,但是对于父进程的修改是不会影响子进程;
#"子"线程与主线程的pid都是一样的
# from threading import Thread
# from multiprocessing import Process
# import os
#
# def work():
#     print('%s is running' %os.getpid())
#
# if __name__ == '__main__':
#     t=Thread(target=work)
#     t=Process(target=work)
#     t.start()
#     print('主线程',os.getpid())


#同一进程内的多个线程共享该进程的资源
from threading import Thread
from multiprocessing import Process
import os
n=100

def work():
    global n
    n=0
if __name__ == '__main__':
    # t=Thread(target=work)
    t=Process(target=work)
    t.start()
    t.join()
    print('主线程',n)

 

开启线程的两种方式

#1 同一进程内的多个线程是共享该进程的资源
#2 创建新的线程开销要远远小于开启新的进程0

直接开启
# from threading import Thread
# from multiprocessing import Process
#
# def work(n):
#     print('%s is running' %n)
#
# if __name__ == '__main__':
#     t=Thread(target=work,args=(1,))
#     # t=Process(target=work,args=(1,))
#     t.start()
#     print('主线程')




在类中开启
from threading import Thread
from multiprocessing import Process

class MyThread(Thread):
    def __init__(self,n):
        super().__init__()
        self.n=n
    def run(self):
        print('%s is running' % self.n)

if __name__ == '__main__':
    t=MyThread(2)
    t.start()
    print('主线程')
开启/调用的方式

 

多线程

多线程在Python内实则就是一个假象,为什么这么说呢,因为CPU的处理速度是很快的,所以我们看起来以一个线程在执行多个任务,每个任务的执行速度是非常之快的,利用上下文切换来快速的切换任务,以至于我们根本感觉不到。

但是频繁的使用上下文切换也是要耗费一定的资源,因为单线程在每次切换任务的时候需要保存当前任务的上下文。

什么时候用到多线程?

首先IO操作是不占用CPU的,只有计算的时候才会占用CPU(譬如1+1=2),Python中的多线程不适合CPU密集型的任务,适合IO密集型的任务(sockt server)。

启动多个线程

主进程在启动之后会启动一个主线程,下面的脚本中让主线程启动了多个子线程,然而启动的子线程是独立的,所以主线程不会等待子线程执行完毕,而是主线程继续往下执行,并行执行。

for i in range(50):
    t = threading.Thread(target=Princ, args=('t-%s' % (i),))
    t.start()

join()

  join()方法可以让程序等待每一个线程之后完成之后再往下执行,又成为串行执行。

import threading
import time
def Princ(String):
    print('task', String)
    time.sleep(1)
for i in range(50):
    t = threading.Thread(target=Princ, args=('t-%s' % (i),))
    t.start()
    # 当前线程执行完毕之后在执行后面的线程
    t.join()

让主线程阻塞,子线程并行执行

import threading
import time
def Princ(String):
    print('task', String)
    time.sleep(2)
# 执行子线程的时间
start_time = time.time()
# 存放线程的实例
t_objs = []
for i in range(50):
    t = threading.Thread(target=Princ, args=('t-%s' % (i),))
    t.start()
    # 为了不让后面的子线程阻塞,把当前的子线程放入到一个列表中
    t_objs.append(t)
# 循环所有子线程实例,等待所有子线程执行完毕
for t in t_objs:
    t.join()
# 当前时间减去开始时间就等于执行的过程中需要的时间
print(time.time() - start_time)

查看主线程与子线程

import threading
class MyThreading(threading.Thread):
    def __init__(self):
        super(MyThreading, self).__init__()
    def run(self):
        print('我是子线程: ', threading.current_thread())
t = MyThreading()
t.start()
print('我是主线程: ', threading.current_thread())
输出如下:
我是子线程: <MyThreading(Thread-1, started 7724)> 我是主线程: <_MainThread(MainThread, started 3680)>
 

查看当前进程的活动线程个数

import threading
class MyThreading(threading.Thread):
    def __init__(self):
        super(MyThreading, self).__init__()
    def run(self):
        print('www.anshengme.com')
t = MyThreading()
t.start()
print('线程个数: ', threading.active_count())

输出如下:
www.anshengme.com # 一个主线程和一个子线程 线程个数: 2
 

 

Event

Event是线程间通信最间的机制之一:一个线程发送一个event信号,其他的线程则等待这个信号。用于主线程控制其他线程的执行。 Events 管理一个flag,这个flag可以使用set ()设置成True或者使用clear()重置为False,wait()则用于阻塞,在flag为True之前。flag默认为False。

选项描述
Event.wait([timeout]) 堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)
Event.set() 将标识位设为Ture
Event.clear() 将标识伴设为False
Event.isSet() 判断标识位是否为Ture

 

import threading

def runthreading(event):
    print("Start...")
    event.wait()
    print("End...")
event_obj = threading.Event()
for n in range(10):
    t = threading.Thread(target=runthreading, args=(event_obj,))
    t.start()

event_obj.clear()
inp = input("True/False?>> ")
if inp == "True":
    event_obj.set()

 

守护线程

  • 无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
  • 需要强调的是:运行完毕并非终止运行
  • 对主进程来说,运行完毕指的是主进程代码运行完毕
  • 对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
  • 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束
  • 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
from threading import Thread
import time
def sayhi(name):
    time.sleep(2)
    print('%s say hello' %name)

if __name__ == '__main__':
    t=Thread(target=sayhi,args=('egon',))
    t.setDaemon(True) #必须在t.start()之前设置
    t.start()

    print('主线程')
    print(t.is_alive())
    '''
    主线程
    True
    '''
例子
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


t1=Thread(target=foo)
t2=Thread(target=bar)

t1.daemon=True
t1.start()
t2.start()
print("main-------")

迷惑人的例子
例二

 

Python GIL(Global Interpreter Lock)

链接:http://www.cnblogs.com/linhaifeng/articles/7449853.html

 

posted @ 2017-10-13 16:36  太上老君门下一只虾  阅读(160)  评论(0编辑  收藏  举报