Python_threading,线程和进程

什么是线程

线程是操作系统能够调度的最小的单位。它包含在进程之中,是进程中的实际运作单位。

一条进程中可以并发多个线程,每个线程执行不同的任务。

什么是进程

进程是系统进行资源分配和调度的最小单位,是指操作系统结构的基础。

调用线程的两种方式

#第一种方式
import threading
import time


def foo(num):  # 定义每个线程要运行的函数

    print("running on number:%s" % num)

    time.sleep(3)
    print('ok foo')


if __name__ == '__main__':#__name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
    t1 = threading.Thread(target=foo, args=(1,))  # 生成一个线程实例,传入了参数num为1
    t2 = threading.Thread(target=foo, args=(2,))  # 生成另一个线程实例,传入了参数num为2

    t1.start()  # 启动线程
    t2.start()  # 启动另一个线程

    print(t1.getName())  # 获取线程名
    print(t2.getName())

继承式调用

#第二种方式
import time
import threading
class mythread(threading.Thread):
    def __init__(self,num):
        threading.Thread.__init__(self)#重写threading.Thread的__init__()方法
        self.num = num

    def run(self):
        print('running on number %s'%self.num)#再此重写了threading.Thread的run方法
        time.sleep(3)
        print('ok run')

if __name__ == '__main__':
    t1 = mythread(1)#第一步:创建一个线程对象t1
    t2 = mythread(2)#第二部:创建一个线程对象t2

    t1.start()#并发执行,开始执行线程t1
    t2.start()#并发执行,开始执行线程t2


#运行结果
# running on number 1
# running on number 2
# ok run
# ok run

join和daemon

import threading
import time
from time import ctime,sleep

#定义函数听歌
def music(func):
    for i in range(2):
        print('开始听歌%s.%s'%(func,ctime()))
        sleep(4)
        print('结束听歌%s.%s'%(func,ctime()))

#定义函数看书
def watch(func):
    for i in range(2):
        print('开始读书%s.%s'%(func,ctime()))
        sleep(5)
        print('结束读书%s.%s'%(func,ctime()))
threads = []
t1 = threading.Thread(target=music,args=('无所谓',))
threads.append(t1)
t2 = threading.Thread(target=watch,args=('狂人日记',))
threads.append(t2)
if __name__ == '__main__':
    for i in threads:
        i.start()
    i.join()
    print('执行完成%s'%ctime())
#由于music和watch在for循环中并发执行
#第一步执行music  接下来 io阻塞4秒钟
# 开始听歌无所谓.Tue Apr 10 22:27:25 2018
#第一步执行读书   接下来Io阻塞5秒钟
# 开始读书狂人日记.Tue Apr 10 22:27:25 2018
#在第四秒的时候执行  听歌结束语句
# 结束听歌无所谓.Tue Apr 10 22:27:29 2018
#紧接着在第四秒和第五秒之间又开始了一个听歌的线程  接下来接着等待 4秒
# 开始听歌无所谓.Tue Apr 10 22:27:29 2018
#在第五秒的时候  结束看书狂人日记
# 结束读书狂人日记.Tue Apr 10 22:27:30 2018
#在第五秒的时候  立马又进来了一个线程开支执行 watch 开始看书  狂人日记  然后接着等待5秒
# 开始读书狂人日记.Tue Apr 10 22:27:30 2018
#在第八秒的时候结束听歌无所谓
# 结束听歌无所谓.Tue Apr 10 22:27:33 2018
#在第十秒的时候结束看书  狂人日记
# 结束读书狂人日记.Tue Apr 10 22:27:35 2018
#等待T2线程执行完了之后开始执行主线程 
# 执行完成Tue Apr 10 22:27:35 2018

join的作用其实就是:在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

import threading
import time
from time import ctime,sleep

#定义函数听歌
def music(func):
    for i in range(2):
        print('开始听歌%s.%s'%(func,ctime()))
        sleep(4)
        print('结束听歌%s.%s'%(func,ctime()))

#定义函数看书
def watch(func):
    for i in range(2):
        print('开始读书%s.%s'%(func,ctime()))
        sleep(5)
        print('结束读书%s.%s'%(func,ctime()))
threads = []
t1 = threading.Thread(target=music,args=('无所谓',))
threads.append(t1)
t2 = threading.Thread(target=watch,args=('狂人日记',))
threads.append(t2)
if __name__ == '__main__':
    for i in threads:
        i.setDaemon(True)
        i.start()
    #i.join()
    print('执行完成%s'%ctime())

此时执行结果是:

开始听歌无所谓.Tue Apr 10 22:47:21 2018

开始读书狂人日记.Tue Apr 10 22:47:21 2018

执行完成Tue Apr 10 22:47:21 2018

setdaemon():守护线程,守护守护的意思就是我要保证在我守护范围内的线程执行完毕,so这个意思就是我守护这个线程执行完毕其他的就不管了,爱咋咋地管你有没有执行完,只要我守护的这个线程执行完了我就要关掉了。

将子线程设置为了守护线程。根据setDaemon()方法的含义,子线程打印内容后便结束了,不管父线程是否执行完毕了

程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法了。

Python中万恶的GIL

在Python中其实并不能实现真正的并行执行,而是多个线程抢占cpu,只能说是并发执行而非并行执行。在Python中同一时刻只能有一个线程使用cpu,但是在Java和Csharp中就是可以实现真正的并行执行的。

用一个例子来看GIL

import threading
#将100减到1,串行的时候是执行一百次,最后执行到1.现在用直接开始一百个线程每次执行减一
def reduce():
    global num
    temp = num
    #print('ok')
    temp -= 1
    num = temp
num = 100
threads = []
for i in range(100):
    t = threading.Thread(target=reduce)
    t.start()
    threads.append(t)
for i in threads:
    i.join()


在计算减法的运算中如果没有print('ok')这个io阻塞结果一直是0

当加上print('ok')这个io阻塞的时候


这个也就说明了在Python执行计算密集型函数的时候及时开再多的线程也是不起作用的,究其根本原因就是有GIL管着,在进行计算密集型函数的时候他的切换线程的速度,大于计算的速度的时候就会发生线程间值覆盖的问题。但是怎么解决这个问题呢,这个时候就需要一把锁把进程来锁住。

同步锁lock

#lock
import threading
#将100减到1,串行的时候是执行一百次,最后执行到1.现在用直接开始一百个线程每次执行减一
def reduce():
    global num
    r.acquire()#执行锁住的操作
    temp = num
    print('ok')
    temp -= 1
    num = temp
    r.release()#执行解锁的操作
num = 100
threads = []
r = threading.Lock()#创建了一把进程锁
for i in range(100):
    t = threading.Thread(target=reduce)
    t.start()
    threads.append(t)
for i in threads:
    i.join()

print('all over',num)

此时加了进程锁lock之后就解决了此类问题,在单单的计算的这一步骤还是相当于串行执行的。

死锁

import threading,time

class myThread(threading.Thread):
    def doA(self):
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        time.sleep(3)
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        lockB.release()
        lockA.release()

    def doB(self):
        lockB.acquire()
        print(self.name,"gotlockB",time.ctime())
        time.sleep(2)
        lockA.acquire()
        print(self.name,"gotlockA",time.ctime())
        lockA.release()
        lockB.release()
    def run(self):
        self.doA()
        self.doB()
if __name__=="__main__":

    lockA=threading.Lock()
    lockB=threading.Lock()
    threads=[]
    for i in range(5):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

此时就会发生死锁的情况,此时的解决办法就是加递归锁

递归锁

就是创建锁的时候使用threading.Rlock()此时使用Rlock()创建的就是一个递归锁,Rlock可以多次执行acquire和release每次执行就像在数学表达式里加了小括号一样,在小括号中加小括号(没有并列每个括号中只有一个括号),每次解锁就需要先解锁最内层的然后一层层往外执行。

条件变量同步condition

有时候在实际操作中有些线程需要满足一定条件之后再去执行。threading.condition提供了条件变量线程支持。

condition除了能有Rlock和lock方法之外,还提供了wait(),notify(),notifyAll(),方法

语法:lock_condition = threading.Condition([Lock/Rlock])

wait():线程释放锁并且进入阻塞状态。

notify():条件创造后调用,通知等待线程池激活一个线程.

notifyAll():条件创造后调用通知线程池,激活所有线程.

#应用场景:建立一个做包子的线程类,一个吃包子的线程类
import threading,time
from random import randint
class zuobaozi(threading.Thread):
    def run(self):
        global L
        while True:
            val = randint(1,100)
            print('做包子',self.name,'append:',str(val),L)
            if lock_con.acquire():
                L.append(val)
                lock_con.notify()
                lock_con.release()
            time.sleep(3)

class chibaozi(threading.Thread):
    def run(self):
        global L
        while True:
            lock_con.acquire()
            if len(L) == 0:
                lock_con.wait()
            print('吃包子',self.name,'delete:',str(L[0]),L)
            del L[0]
            lock_con.release
            time.sleep(1)

if __name__ == '__main__':
    L = []
    lock_con = threading.Condition()
    threads = []
    for i in range(5):
        threads.append(zuobaozi())
    threads.append(chibaozi())
    for t in threads:
        t.start()
    for i in threads:
        i.join()

同步条件 Event

event其实和condition是差不多的只不过没有了condition的锁的功能.设计于不访问共享资源的条件环境.event = threading.Event(bool)

isSet():返回event的状态值

event.wait():如果状态值是false则阻塞

enent.set():设置状态值为True,将阻塞线程池的线程激活进入就绪状态.

event.clear():恢复状态值为false

import threading,time
class Boss(threading.Thread):
    def run(self):
        print("BOSS:今晚大家都要加班到22:00。")
        event.isSet() or event.set()
        time.sleep(5)
        print("BOSS:<22:00>可以下班了。")
        event.isSet() or event.set()
class Worker(threading.Thread):
    def run(self):
        event.wait()
        print("Worker:哎……命苦啊!")
        time.sleep(0.25)
        event.clear()
        event.wait()
        print("Worker:OhYeah!")
if __name__=="__main__":
    event=threading.Event()
    threads=[]
    for i in range(5):
        threads.append(Worker())
    threads.append(Boss())
    for t in threads:
        t.start()
    for t in threads:
        t.join()
BOSS:今晚大家都要加班到22:00。
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
Worker:哎……命苦啊!
BOSS:<22:00>可以下班了。
Worker:OhYeah!
Worker:OhYeah!
Worker:OhYeah!
Worker:OhYeah!

Worker:OhYeah!

信号量Semaphore

    信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
    计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)

     BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常

import threading
import time
class MyThread(threading.Thread):
    def run(self):
        if semphores.acquire():
            print(self.name)
            time.sleep(5)
            semphores.release()

if __name__ == '__main__':
    semphores = threading.Semaphore(5)
    threads = []
    for i in range(100):
        threads.append(MyThread())
    for t in threads:
        t.start()

多线程利器queue

queue就是一种数据类型,这种数据类型的特点就是先进先出,FIFO,是一种堆栈的结构,创建queue的时候需要指定队列的长度,当队列中的元素个数达到制定的个数的时候,接下来的就会进入等待,知道空出一个位置出来

常用方法

增:queue.put()

查:queue.get()

队列大小:queue.qsize()

判断是否是空:queue.empty()

判断队列是否满了:queue.full()

等待对列为空然后执行别的操作:queue.join()

import threading,queue
from time import sleep
from random import randint
class Production(threading.Thread):
    def run(self):
        while True:
            r=randint(0,100)
            q.put(r)
            print("生产出来%s号包子"%r)
            sleep(1)
class Proces(threading.Thread):
    def run(self):
        while True:
            re=q.get()
            print("吃掉%s号包子"%re)
if __name__=="__main__":
    q=queue.Queue(10)
    threads=[Production(),Production(),Production(),Proces()]
    for t in threads:
        t.start()

进程:

进程和线程的用法基本相同,就不巴拉巴拉说一大堆了.在进程中没有GIL所以使用进程可以实现真正的并发执行.

进程的两种调用方式:

第一种方式:

from multiprocessing import Process
import time
def foo(name):
    time.sleep(1)
    print('hello',name,time.ctime())
if __name__ == '__main__':
    p_list = []
    for i in range(5):
        p = Process(target=foo,args=('sign',))
        p_list.append(p)
        p.start()
    for t in p_list:
        t.join()
    print('end')
hello sign Sat Apr 14 10:53:09 2018
hello sign Sat Apr 14 10:53:09 2018
hello sign Sat Apr 14 10:53:09 2018
hello sign Sat Apr 14 10:53:09 2018
hello sign Sat Apr 14 10:53:09 2018

end

第二种方式

from multiprocessing import Process
import time
class myprocess(Process):
        def __init__(self):
            super(myprocess,self).__init__()

        def run(self):
            time.sleep(1)
            print('hello',self.name,time.ctime())
if __name__ == '__main__':
    p_list = []
    for i in range(5):
        p = myprocess()
        p.start()
        p_list.append(p)

    for t in p_list:
        t.join()
    print('end')
hello myprocess-1 Sat Apr 14 11:04:03 2018
hello myprocess-2 Sat Apr 14 11:04:03 2018
hello myprocess-4 Sat Apr 14 11:04:03 2018
hello myprocess-3 Sat Apr 14 11:04:03 2018
hello myprocess-5 Sat Apr 14 11:04:03 2018

end

Process类

构造方法:    

Process([group [, target [, name [, args [, kwargs]]]]])

  target:要执行的方法名

      name:进程名

      args:参数

实例方法:

is_alive():判断进程是否还在运行

join():与线程的join作用相同

run():start()调用run()方法

start():进入准备cpu开始调用

terminate():不管任务是否完成理解结束任务.

属性:

authkey

daemon:和线程的setDeamon功能一样

exitcode(进程在运行时为None、如果为–N,表示被信号N结束)

name:进程名字。

pid:进程号。

def foo(i):
    global p
    time.sleep(1)
    print (p.is_alive(),i,p.pid)
    time.sleep(1)

if __name__ == '__main__':
    p_list=[]
    for i in range(10):
        p = Process(target=foo, args=(i,))
        p.daemon=True
        p_list.append(p)

    for p in p_list:
        p.start()
    for p in p_list:
        p.join()

进程间的通信

使用Queues

使用方法类似于threading里的queue,但是含义却是大不相同

from multiprocessing import Process, Queue

def f(q,n):
    q.put([42, n, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p_list=[]
    for i in range(3):
        p = Process(target=f, args=(q,i))
        p_list.append(p)
        p.start()
    print(q.get())
    print(q.get())
    print(q.get())
    for i in p_list:
            i.join()

使用pipes进行进程间通信

#使用pipes进行进程间通信
from multiprocessing import Process,Pipe
import time
def f(conn):
    conn.send('约吗?')
    print(conn.recv())
    conn.close()
if __name__ == '__main__':
    parent_conn,children_conn = Pipe()
    p = Process(target=f,args=(children_conn,))
    p.start()
    print(parent_conn.recv())
    parent_conn.send('滚!!!')
    p.join()

使用pipe进行线程间通讯更类似于socket的通信.


协程:

协程只有一个线程,省去了上下文切换的消耗,不需要锁了再,

高并发+高扩展性+低成本:一个cpu支持上完的协程都不是问题,所以很适合高并发处理

但是,无法利用多核资源,和进行阻塞的时候



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值