线程

线程被称作轻量级的进程。  GIL:全局解释锁(只有Cpython解释器才有)
对于线程来说,因为有了GIL,所以没有真正的并行

计算机的执行单位以线程为单位。计算机的最小可执行是线程。
进程是资源分配的基本单位。线程是可执行的基本单位,是可被调度的基本单位。
线程不可以自己独立拥有资源。线程的执行,必须依赖于所属进程中的资源。
进程中必须至少应该有一个线程。

线程又分为用户级线程和内核级线程(了解)
用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度
内核级线程:对于计算机内核来说的,这样的线程完全被内核控制调度。

进程由 代码段 数据段 PCB组成(process control block)
线程由 代码段 数据段 TCB组成(thread control block)
线程和进程的比较
thread - 线程
import thread 操作线程的模块
import threading 用这个去操作线程
(1) cpu切换进程要比cpu切换线程 慢很多
在python中,如果IO操作过多的话,使用多线程最好了
(2) 在同一个进程内,所有线程共享这个进程的pid,也就是说所有线程共享所属进程的所有资源和内存地址
(3) 在同一个进程内,所有线程共享该进程中的全局变量

(4) 因为有GIL锁的存在,在Cpython中,没有真正的线程并行。但是有真正的多进程并行
当你的任务是计算密集的情况下,使用多进程好
总结:在CPython中,IO密集用多线程,计算密集用多进程

(5)关于守护线程和守护进程的事情(注意:代码执行结束并不代表程序结束)
守护进程:要么自己正常结束,要么根据父进程的代码执行结束而结束
守护线程:要么自己正常结束,要么根据父线程的执行结束而结束
线程与进程的区别可以归纳为以下4点:
  1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
  3)调度和切换:线程上下文切换比进程上下文切换要快得多。
  4)在多线程操作系统中,进程不是一个可执行的实体。

线程的特点

  在多线程的操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。
  1)轻型实体
  线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
  线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。
TCB包括以下信息:
(1)线程状态。
(2)当线程不运行时,被保存的现场资源。
(3)一组执行堆栈。
(4)存放每个线程的局部变量主存区。
(5)访问同一个进程中的主存和其它资源。
用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
TCP包涵信息
 2)独立调度和分派的基本单位。
  在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
  3)共享进程资源。
  线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
  4)可并发执行。
  在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。

python和线程

理论知识

全局解释器锁GIL

  Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
  对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

  在多线程环境中,Python 虚拟机按以下方式执行:

  a、设置 GIL;

  b、切换到一个线程去运行;

  c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));

  d、把线程设置为睡眠状态;

  e、解锁 GIL;

  d、再次重复以上所有步骤。
  在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。

python线程模块的选择

  Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
  避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。 

  thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。

threading模块

multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍(官方链接

需注意,玩多线程编程时,可以不用再像写多进程一样,每个py文件中必须加上if __name__ == '__main__'这一行代码了

线程的创建Threading.Thread类

from threading import Thread
def func():
    print("我是线程")
t =Thread(target=func)
t.start()
自定义类开启线程
from threading import Thread
class MyTheard(Thread):
    def __init__(self):
        super(MyTheard, self).__init__()
    def run(self):
        print("我是线程")
t = MyTheard()
t.start()
主进程会等线程执行完后结束
from threading import Thread
import time
def func():
    print("我是线程")
    time.sleep(2)
t = Thread(target=func)
t.start()

多线程与多进程

from threading import Thread
from multiprocessing import Process
import os
import time
def son(n):
    print('%s中,我是儿子,我的pid号是: %s'%(n,os.getpid()))

if __name__ == '__main__':
    t = Thread(target=son,args=('线程',))
    t.start()
    print('线程中,我是爸爸,我的pid号是:%s'%os.getpid())

    time.sleep(2)
    p = Process(target=son,args=('进程',))
    p.start()
    print('进程中,我是爸爸,我的pid号是:%s' % os.getpid())

线程与进程开启的效率对比

from threading import Thread
from multiprocessing import Process
import time
def func():
    pass
if __name__ == '__main__':
    start = time.time()
    for i in range(100):
        t = Thread(target=func)
        t.start()
    print(time.time()-start)
    start = time.time()
    for i in range(100):
        p = Process(target=func)
        p.start()
    print(time.time() - start)

共享数据的比较

from threading import Thread
import time

def func():
    global num
    num-=1
    time.sleep(1)

if __name__ == '__main__':
    num = 100
    t = Thread(target=func,)
    t.start()
    t.join()
    print(num)
# 结论:很明显,多线程是一个共享一个进程内部的全局变量

线程---信号量

from threading import Thread,Semaphore
import time
def func(s,i):
    s.acquire()
    print("第%s个人进屋子" %i)
    time.sleep(2)
    print("第%s个人出屋子" %i)
    s.release()
if __name__ == '__main__':
    s = Semaphore(20)
    for i in range(20):
        t = Thread(target=func,args=(s,i))
        t.start()

线程----互斥锁

死锁演示

from threading import Thread,Lock
from multiprocessing import Process
import time
def man(l_paper,l_toilet):
    l_toilet.acquire()
    print("小明在厕所上厕所")
    time.sleep(1)
    l_paper.acquire()
   print("小明拿到卫生纸了") time.sleep(
0.5) print("小明上完厕所了") l_toilet.release() l_toilet.release() def women(l_paper,l_toilet): l_paper.acquire() print("小红拿到了卫生纸") time.sleep(1) l_toilet.acquire() print("小红在厕所上厕所") time.sleep(0.5) print("小红上完厕所了") l_toilet.release() l_toilet.release() if __name__ == '__main__': l_paper = Lock() l_toilet = Lock() t_man = Thread(target=man,args=(l_paper,l_toilet)) t_women = Thread(target=women,args=(l_paper,l_toilet)) t_man.start() t_women.start()

解决死锁

from threading import Thread,RLock
from multiprocessing import Process
import time
def man(l_paper,l_toilet):
    l_toilet.acquire()
    print("小明在厕所上厕所")
    time.sleep(1)
    l_paper.acquire()
    print("小明拿到卫生纸了")
    time.sleep(0.5)
    print("小明上完厕所了")
    l_toilet.release()
    l_toilet.release()
def women(l_paper,l_toilet):
    l_paper.acquire()
    print("小红拿到了卫生纸")
    time.sleep(1)
    l_toilet.acquire()
    print("小红在厕所上厕所")
    time.sleep(0.5)
    print("小红上完厕所了")
    l_toilet.release()
    l_toilet.release()

if __name__ == '__main__':
    l_paper = l_toilet = RLock()
    t_man = Thread(target=man,args=(l_paper,l_toilet))
    t_women = Thread(target=women,args=(l_paper,l_toilet))
    t_man.start()
    t_women.start()

递归锁(万能钥匙)

from threading import RLock
r = RLock()
r.acquire()
r.acquire()
r.acquire()
r.acquire()
r.acquire()
r.acquire()
r.acquire()
r.acquire()
print(123456789)
定时器
Timer(time,function)
time:睡眠时间,以秒为单位
function:睡眠时间之后需要执行的任务
from threading import Timer
def func():
    print("就是这么帅")
Timer(3,func).start()

线程-----事件

from threading import Thread,Event
import time,random


def connect(e,i):
    count = 1
    while count <= 3:
        if e.is_set():
            print("第%s个人连接成功." % i)
            break
        print("正在尝试第%s次连接......" %(count))
        e.wait(0.5)
        count+=1

def update(e):
    print("\033[32m 数据库正在更新中 \033[0m")
    time.sleep(random.randint(1,2))
    e.set()

if __name__ == '__main__':
    e =Event()
    t_update = Thread(target=update,args=(e,))
    t_update.start()

    for i in range(10):
        t_connect = Thread(target=connect,args=(e,i))
        t_connect.start()
使用条件机制调度线程
Condition涉及4个方法

acquire()
release()
wait()
notify(int)是指给wait发一个信号,让wait变成不阻塞
int是指,发多少个信号给wait
from threading import Thread,Condition
def func(c,i):
    c.acquire()
    c.wait()
    c.release()
    print("第%s个线程开始执行了." %i)
if __name__ == '__main__':
    c = Condition()
    for i in range(10):
        t = Thread(target=func,args = (c,i))
        t.start()
    while 1:
        num = int(input(">>>"))
        c.acquire()
        c.notify(num)
        c.release()

守护线程

def func():
    time.sleep(2)
    print(123)
def func1():
    time.sleep(5)
    print(456789)
if __name__ == '__main__':
    t = Thread(target=func,)
    t.daemon = True
    t.start()
    t1 = Thread(target=func1,)
    t1.start()
    print(666666)

 









 
 
 
 
posted @ 2018-08-23 17:17  chenyibai  阅读(323)  评论(0编辑  收藏  举报