python基础---->进程、线程及相关等


基本概念

进程

1、什么是进程?

  程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。

  程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

  简单点说就是:进程就是一个程序在一个数据集上的一次动态执行过程。

2、进程的特点:

  一个时间点只能做一件事,不能同时干两件及以上的事;

  在执行的过程进程如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

线程

1、什么是线程(Thread)?

  线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元

  一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务;

2、线程的特点:

  突破一个进程只能干一样事的缺陷,使到进程内并发成为可能;

  线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能; 

进程和线程的区别 

  线程是执行的指令集 , 进程是资源的集合

  线程的启动速度要比进程的启动速度要快

  两个线程的执行速度是一样的,进程与线程的运行速度是没有可比性的

  线程共享创建它的进程的内存空间 , 进程的内存是独立的

  两个线程共享的数据都是同一份数据 , 两个子进程的数据不是共享的 , 而且数据是独立的

  同一个进程的线程之间可以直接交流 , 同一个主进程的多个子进程之间是不可以进行交流 , 如果两个进程之间需要通信 , 就必须要通过一个中间代理来实现

  一个新的线程很容易被创建 , 一个新的进程创建需要对父进程进行一次克隆

  一个线程可以控制和操作同一个进程里的其他线程 , 线程与线程之间没有隶属关系 , 但是进程只能操作子进程

  改变主线程 , 有可能会影响到其他线程的行为 , 但是对于父进程的修改是不会影响子进程

python threading模块

线程有两种调用方式:

直接调用:

import threading
import time

def run(n):         #定义线程要运行的函数
    print('task',n)
    time.sleep(2)

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))          #生成一个线程
    t2 = threading.Thread(target=run,args=(2,))          #生成一个另外线程
    
    t1.start()    # 启动线程
    t2.start()    # 启动另外一个线程

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

print('I am main thread')                #主线程

#这个进程里面有三个线程,1个主线程,t1,t2两个子线程
#子线程和主线程是同步开启的,主线程结束后,要等子线程全部结束后,进程才会关闭

PS:线程相关的方法:

start        线程准备就绪,等待CPU调度
setName      为线程设置名称
getName      获取线程名称
run          线程被cpu调度后自动执行线程对象的run方法

# 下面会讲到
setDaemon    将线程声明为守护线程,必须在start() 方法调用之前设setDaemon(),只要主线程完成了,不管子线程是否完成,都要和主线程一起退出
join         逐个执行每个线程,执行完毕后继续往下执行,该方法保证当前线程执行完成后再执行其它线程,使得多线程变得无意义。

继承式调用:

import threading
import time

class MyThread(threading.Thread):   #继承threading,Thread模块
    def __init__(self,n):
        threading.Thread.__init__()    #继承父类
        # 或者super(MyThread, self).__init__() 

        self.n = n

    def run(self):                 #定义每个线程要运行的函数,必须用run
        print('task',self.n)
        time.sleep(2)


if  __name__ == '__main__':
    t1 = MyThread(1)
    t2 = MyThread(2)

    t1.start()
    t2.start()

print('I am main thread')

jion和setDaemon:

主线程:当一个程序启动时 , 就有一个进程被操作系统创建 , 与此同时一个线程也立刻运行 , 该线程通常叫做程序的主线程;

子线程 : 因为程序是开始时就执行的 , 如果你需要再创建线程 , 那么创建的线程就是这个主线程的子线程;

join应用:

import threading
import time

def run(n):
    print("task ",n )
    time.sleep(2)

start_time = time.time()
t_objs = []    # 存线程实例

for i in range(10):        #生成10个线程
    t = threading.Thread(target=run,args=("t-%s" %i ,))
    t.start()
    t_objs.append(t)  #为了不阻塞后面线程的启动,不在这里join,先放到一个列表里

for t in t_objs:      #循环线程实例列表,等待所有线程执行完毕
    t.join()

print("---all threads has finished...")
print("cost:",time.time() - start_time)

setDaemon应用:

import threading
import time

def run(n):
    print('task',n)
    time.sleep(2)
    print('i am 子线程')     #主线程结束,setDaemon不管有没有运行完都会被销毁

if __name__ == '__main__':
    t1 = threading.Thread(target=run,args=(1,))
    t2 = threading.Thread(target=run,args=(2,))
    t1.setDaemon(True)   #设置守护线程,放在start之前
    t1.start()
    t2.setDaemon(True)
    t2.start()

print('I am main thread')

结果:
task 1
task 2
I am main thread

线程锁

lock:如果有多个进程对同一文件进行修改 , 就会造成错乱 , 所以我们为了保护文件数据的安全 , 就需要给其进行加锁,join为整体串行 , lock为局部串行

Rlock(递归锁):在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。就是解决死锁的问题

加锁:

import time
import threading

def addNum():
    global num #在每个线程中都获取这个全局变量
    print('--get num:',num )
    time.sleep(1)
    lock.acquire() #修改数据前加锁
    num  -=1 #对此公共变量进行-1操作
    lock.release() #修改后释放

num = 100  #设定一个共享变量
thread_list = []
lock = threading.Lock() #生成全局锁
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有线程执行完毕
    t.join()

print('final num:', num )

Semaphore(信号量)

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如食堂打饭有5个窗口,那最多只允许5个人同时打饭,后面的人只能等里面有人打完才能去打。

import threading, time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" % n)
    semaphore.release()

if __name__ == '__main__':
    semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run, args=(i,))
        t.start()
while threading.active_count() != 1:
    pass
else:
    print('----all threads done---')
semaphore

Queue(队列)

Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递

基本FIFO队列

FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。

简单的FIFO:

import Queue

q = Queue.Queue()

for i in range(5):
    q.put(i)

while not q.empty():
    print q.get()
0
1
2
3
4
结果

LIFO队列

LIFO即Last in First Out,后进先出。与栈的类似;

简单的LIFO:

import Queue

q = Queue.LifoQueue()

for i in range(5):
    q.put(i)

while not q.empty():
    print q.get()
4
3
2
1
0
结果

优先级队列

import Queue
import threading

class Job(object):
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print('Job:',description)
        return
    def __cmp__(self, other):
        return cmp(self.priority, other.priority)

q = Queue.PriorityQueue()

q.put(Job(3, 'level 3 job'))
q.put(Job(10, 'level 10 job'))
q.put(Job(1, 'level 1 job'))

def process_job(q):
    while True:
        next_job = q.get()
        print('for:', next_job.description)
        q.task_done()

workers = [threading.Thread(target=process_job, args=(q,)),
        threading.Thread(target=process_job, args=(q,))
        ]

for w in workers:
    w.setDaemon(True)
    w.start()

q.join()
Job: level 3 job
Job: level 10 job
Job: level 1 job
for: level 1 job
for: level 3 job
for: job: level 10 job
结果

常用方法

qsize()          
#看队列大小

task_done()
# 意味着之前入队的一个任务已经完成。由队列的消费者线程调用。
# 每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。
join() # 阻塞调用线程,直到队列中的所有任务被处理掉。只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。 put(item[, block[, timeout]]) # 将item放入队列中。 # 1、如果可选的参数block为True且timeout为空对象(默认的情况,阻塞调用,无超时)。 # 2、如果timeout是个正整数,阻塞调用进程最多timeout秒,如果一直无空空间可用,抛出Full异常(带超时的阻塞调用)。 # 2、如果block为False,如果有空闲空间可用将数据放入队列,否则立即抛出Full异常其非阻塞版本为put_nowait等同于put(item, False) get([block[, timeout]]) # 从队列中移除并返回一个数据。block跟timeout参数同put方法 # 其非阻塞方法为`get_nowait()`相当与get(False) full() #判断队列是否有数据 返回bool值 empty() # 如果队列为空,返回True,反之返回False

Event事件

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

# -*- coding:utf-8 -*-
# @Author  : Clint
import threading


def do(event):
    print('start')
    
    event.wait()
    print('execute')


event_obj = threading.Event()
for i in range(10):
    t = threading.Thread(target=do, args=(event_obj,))
    t.start()

event_obj.clear()
inp = input('input:')
if inp == 'true':
    event_obj.set()

Condition(条件)

使得线程等待,只有满足某条件时,才释放n个线程

def condition_func():

    ret = False
    inp = input('>>>')
    if inp == '1':
        ret = True

    return ret


def run(n):
    con.acquire()
    con.wait_for(condition_func)
    print("run the thread: %s" %n)
    con.release()

if __name__ == '__main__':

    con = threading.Condition()
    for i in range(10):
        t = threading.Thread(target=run, args=(i,))
        t.start()

Timer(定时器)

from threading import Timer
 
 
def hello():
    print("hello, world")
 
t = Timer(1, hello)
t.start()  # 一秒后打印

进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池中有两个方法:apply、apply_async

# -*- coding:utf-8 -*-
# @Author  : Clint
from multiprocessing import Process, Pool
import time


def Foo(i):
    time.sleep(2)
    return i + 100


def Bar(arg):
    print(arg)


pool = Pool(5)
# print pool.apply(Foo,(1,))
# print pool.apply_async(func =Foo, args=(1,)).get()

for i in range(10):
    pool.apply_async(func=Foo, args=(i,), callback=Bar)

print('end')

pool.close()
pool.join()   # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。

猜你喜欢

转载自www.cnblogs.com/Utopia-Clint/p/10885551.html