python多线程与多进程基础

一、python多线程的基本使用

1、多线程的调用方式:

①、直接调用:

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    print args
    time.sleep(1)
# 直接调用,target后面接线程启动的目标函数,args后面接目标函数的参数,必须以元组的形式
t = threading.Thread(target=run, args=('args', ))
# 启动子线程
t.start()

②、类调用(继承调用):

# -*- coding:utf-8 -*-

import threading
import timedef controller(args):
    # 打印当前线程名
    print threading.current_thread().getName()
    # 打印当前活动的线程数
    print threading.active_count()
    print args
    time.sleep(1)


# 自定义线程类,并继承threading.Thread
class MyThread(threading.Thread):
    def __init__(self, args):
        # 封装调用controller函数的参数
        self.args = args
        # 使用父类的构造方法
        super(MyThread, self).__init__()

    # 重写线程调用时执行的run方法
    def run(self):
        controller(self.args)
        print 'controller函数执行了'

# 程序内打印__name__的值为__main__,程序外调用__name__的值为程序名
if __name__ == '__main__':
    # 实例化自定义的线程类
    t = MyThread('args')
    # 启动线程子线程
    t.start()

    print 'main thread running'

2、主线程的等待join方法和设置守护线程:

①、多线程的join()方法:

前面的脚本运行后,你就会发现,子线程还没有执行完,主线程已经退出了,那么就做不到线程之间的数据同步;使用join方法,让主线程等待子线程全部退出后再执行自己的代码段:

# -*- coding:utf-8 -*-

import threading
import time
import sys


def controller(args):
    # 打印当前线程名
    print threading.current_thread().getName()
    # 打印当前活动的线程数
    print threading.active_count()
    print args
    time.sleep(1)


# 自定义线程类,并继承threading.Thread
class MyThread(threading.Thread):
    def __init__(self, args):
        # 封装调用controller函数的参数
        self.args = args
        # 使用父类的构造方法
        super(MyThread, self).__init__()

    # 重写线程调用时执行的run方法
    def run(self):
        controller(self.args)

# 程序内打印__name__的值为__main__,程序外调用__name__的值为程序名
if __name__ == '__main__':
    thread_pool = []
    for i in range(10):
        # 实例化自定义的线程类
        t = MyThread('args')
        # 把新创建的线程加入到线程池
        thread_pool.append(t)
        # 启动线程子线程
        t.start()
    for thread in thread_pool:
        # 主线程等待所有子线程退出
        thread.join()

    print 'main thread running'

②设置守护线程 setDaemon():

子线程设置为了守护线程就变成了主线程的仆人,主线程一停止,不管子线程是否执行完毕,都会被停止:

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    print args
    time.sleep(3)
    print '设置守护线程这里将不会执行'

# 直接调用,target后面接线程启动的目标函数,args后面接目标函数的参数,必须以元组的形式
t = threading.Thread(target=run, args=('args', ))

# 设置守护进行,主线程执行完毕后,子线程自动退出
t.setDaemon(True)
# 启动子线程
t.start()

time.sleep(1)
print 'main thread running...'

3、使用线程锁:

因为python多线程的全局解释器锁的存在,同一时间只有一个线程在执行,但是多个线程调用的时候,他们都会拷贝走一份数据,前一个线程未执行完,如果下一个线程就获得GIL锁的话,数据就会修改错误,所以在这里可以给每个线程在加一把锁,用于保护文件数据的修改:

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    # 获得锁
    # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕
    lock.acquire()
    print args
    time.sleep(3)
    # 释放锁
    lock.release()
    print '设置守护线程这里将不会执行'

# 生成一把线程锁
lock = threading.Lock()

t = threading.Thread(target=run, args=('args', ))

# 启动子线程
t.start()
t.join()
time.sleep(1)
print 'main thread running...'

如果遇到要使用多把锁的情况,可以使用递归锁:

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    # 获得锁
    # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕
    lock.acquire()
    print args
    run1('run1')
    time.sleep(3)
    # 释放锁
    lock.release()
    print '设置守护线程这里将不会执行'


def run1(args):
    # 获得锁
    # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕
    lock.acquire()
    print args
    time.sleep(3)
    # 释放锁
    lock.release()
    print '设置守护线程这里将不会执行1'


# 生成一把递归线程锁
lock = threading.RLock()

t = threading.Thread(target=run, args=('args', ))

# 启动子线程
t.start()
t.join()
time.sleep(1)
print 'main thread running...'

4、信号量 Semaphore:

lock线程锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据;

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    # 获得锁
    # 程序执行到线程锁中间的代码段,每个线程都会等待上一个线程执行完毕
    semaphore.acquire()
    print args
    time.sleep(3)
    # 释放锁
    semaphore.release()

# 设置信号量
semaphore = threading.BoundedSemaphore(5)

for i in range(20):
    t = threading.Thread(target=run, args=('args', ))
    # 启动子线程
    t.start()

time.sleep(1)
print 'main thread running...'

 5、延时启动 Timer:

# -*- coding:utf-8 -*-

import threading
import time


def run(args):
    print args
    time.sleep(3)


for i in range(5):
    # 延时启动
    t = threading.Timer(3.0, run, args=('args', ))
    # 启动子线程
    t.start()

time.sleep(1)
print 'main thread running...'

6、Events 事件:

多线程中可以通过Event来实现两个或多个线程间的交互,通过event设置标志位,线程通过不断检测标志位是否被设定,来判断是否执行,因此可以用来调度多个线程的运行情况;

相关方法有:

# 创建一个事件的实例
event = threading.Event()

event.set()      # 设置标志位
event.clear()    # 清除标志位
event.wait()     # 等待标志位被设定
event.is_set()   # 判断标志位是否被设定

实例demo:

# -*- coding:utf-8 -*-

import threading
import time


def logcat(args):
    while True:
        if event.is_set():
            print '标志位被设定,线程运行。。。'
            time.sleep(1)
        else:
            print '标志位未被设定,等待中。。。'
            # 未检测到标志位,进行阻塞
            event.wait()


# 创建一个函数,用于控制标志位
def run():
    event.set()
    count = 0
    while True:
        if 5 < count < 10:
            event.clear()
        elif count > 10:
            event.set()
            count = 0
        count += 1
        time.sleep(1)


# 创建一个事件的实例
event = threading.Event()

t1 = threading.Thread(target=run,)
t2 = threading.Thread(target=logcat, args=('adb devices',))
t1.start()
t2.start()

 二、queue队列 

使用队列,进行线程间的通信;

1、实例化一个队列,并指定队列的长度:

# -*- coding:utf-8 -*-
import Queue

# 实例化一个队列,接受参数为队列的最大长度
q = Queue.Queue(maxsize=5)

2、往队列中添加数据:

q.put(1)  # 往队列中put数据,超过最大长度,则会阻塞,直到有数据被取出

3、从队列中取出数据:

q.get()  # 取出一条数据

4、查看队列的状态:

q.full()  # 判断队列是否已满,返回布尔值

q.empty()  # 判断队列是否为空,返回布尔值

q.qsize()  # 返回队列的长度

5、设置不同规则队列的:

①先入先出队列:

②、后入先出队列:

③、自定义出列顺序:

 三、多进程的基本使用

1、创建进程:

# -*- coding:utf-8 -*-

from multiprocessing import Process
import time
import os


def run():
    print 'process running'
    # 打印当前进程的进程号
    print os.getpid()
    time.sleep(2)

# windows启动多进程需要加下面这行
if __name__ == '__main__':
    # 创建一个进程
    p = Process(target=run,)
    p.start()
    p.join()
    print 'main process running '

2、进程间通讯:

①、进程的队列Queue:

# -*- coding:utf-8 -*-

from multiprocessing import Process, Queue
import time


def run(q):
    q.put(23)
    time.sleep(2)


if __name__ == '__main__':
    # 创建一个进程Q
    q = Queue(maxsize=5)
    '''通过参数进行传递队列,因为不同进程间的内存地址不相同,
    因此不能同时共享一份数据,这里是将创建的队列先序列化,传过去,再序列化传回来'''
    p = Process(target=run, args=(q, ))
    p.start()
    print q.get()

②、管道Pipe:

通过类似socket的方式进行数据的传递:

# -*- coding:utf-8 -*-

from multiprocessing import Process, Pipe


def run(p):
    text = p.recv()
    print text
    p.send('hello, father')


if __name__ == '__main__':
    # 新建一个管道,获得管道的两端,一端给子进程,一端父进程拿着
    father, son = Pipe()
    p = Process(target=run, args=(son, ))
    p.start()
    # 通过send和recv来接收,发送数据
    father.send('hello, son')
    print father.recv()

③、Managers 数据类型管理:

支持的数据类型: listdictNamespaceLockRLockSemaphoreBoundedSemaphoreConditionEventBarrierQueueValue and Array

# -*- coding:utf-8 -*-

from multiprocessing import Process, Manager


def run(l1, d1):
    l1.append(10)
    d1['12'] = '23'


if __name__ == '__main__':
    # 创建一个进程间可以数据共享的对象
    manager = Manager()
    # 生成一个进程间可以数据共享的列表
    list1 = manager.list()
    # 生成一个进程间可以数据共享的字典
    dict1 = manager.dict()
    p = Process(target=run, args=(list1, dict1))
    p.start()
    p.join()
    print list1, dict1

3、进程池:

因为在windows上开多了进程,系统会爆炸的,所以需要有个进程池;

# -*- coding:utf-8 -*-


from multiprocessing import Process, Pool
import time


def f(i):
    time.sleep(1)
    return i


# 回调函数
def run(arg):
    print('-->callback:', arg)


if __name__ == '__main__':
    pool = Pool(5)
    for i in range(10):
        # 进程池,并可以指定父进程执行的回调函数
        pool.apply_async(func=f, args=(i,), callback=run)

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

 

posted @ 2017-07-06 11:03  还是原来那个我  阅读(195)  评论(0编辑  收藏  举报