python多线程、锁、event事件机制的简单使用

线程和进程

一、线程共享建立它的进程的地址空间,进程有本身的地址空间

二、线程能够访问进程全部的数据,线程能够相互访问app

三、线程之间的数据是独立的ide

四、子进程复制线程的数据函数

五、子进程启动后是独立的 ,父进程只能杀掉子进程,而不能进行数据交换ui

六、修改线程中的数据,都是会影响其余的线程,而对于进程的更改,不会影响子进程线程

threading.Thread

Thread 是threading模块中最重要的类之一,可使用它来建立线程。有两种方式来建立线程:一种是经过继承Thread类,重写它的run方法;另外一种是建立一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象做为参数传入。

先来看看经过继承threading.Thread类来建立线程的例子:code

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, arg):
        # super(MyThread, self).__init__() # 新式类继承原有方法写法
        threading.Thread.__init__(self)
        self.arg = arg

    def run(self):
        time.sleep(2)
        print(self.arg)

for i in range(10):
    thread = MyThread(i)
    print(thread.name)
    thread.start()

另一种建立线程的方法:对象

import threading
import time

def process(arg):
    time.sleep(2)
    print(arg)

for i in range(10):
    t = threading.Thread(target=process, args=(i,))
    print(t.name)
    t.start()

Thread类还定义了如下经常使用方法与属性:

Thread.getName() 获取线程名称
Thread.setName() 设置线程名称
Thread.name 线程名称

Thread.ident 获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法以后该属性才有效,不然它只返回None继承

判断线程是不是激活的(alive)。从调用start()方法启动线程,到run()方法执行完毕或遇到未处理异常而中断 这段时间内,线程是激活的进程

Thread.is_alive()
Thread.isAlive()内存

Thread.join([timeout]) 调用Thread.join将会使主调线程堵塞,直到被调用线程运行结束或超时。参数timeout是一个数值类型,表示超时时间,若是未提供该参数,那么主调线程将一直堵塞到被调线程结束

Python GIL(Global Interpreter Lock)

GIL并非Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就比如C++是一套语言(语法)标准,可是能够用不一样的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也同样,一样一段代码能够经过CPython,PyPy,Psyco等不一样的Python执行环境来执行。像其中的JPython就没有GIL。然而由于CPython是大部分环境下默认的Python执行环境。因此在不少人的概念里CPython就是Python,也就想固然的把GIL归结为Python语言的缺陷。因此这里要先明确一点:GIL并非Python的特性,Python彻底能够不依赖于GIL。

线程锁的使用:

# 锁:GIL 全局解释器 它是为了保证线程在运行过程当中不被抢占
number = 0
lock = threading.RLock()    # 建立锁


def run(num):
    lock.acquire()  # 加锁
    global number
    number += 1
    print(number)
    time.sleep(2)
    lock.release()  # 释放锁

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

Join & Daemon

主线程A中,建立了子线程B,而且在主线程A中调用了B.setDaemon(),这个的意思是,把主线程A设置为守护线程,这时候,要是主线程A执行结束了,就无论子线程B是否完成,一并和主线程A退出.这就是setDaemon方法的含义,这基本和join是相反的。此外,还有个要特别注意的:必须在start() 方法调用以前设置,若是不设置为守护线程,程序会被无限挂起。

class MyThread1(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        print("thread start")
        time.sleep(3)
        print('thread end')

print('main start')
thread1 = MyThread1()
# thread1.setDaemon(True)     # 设置子线程是否跟随主线程一块儿结束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join()    # 使主线程阻塞,直至子线程运行完毕再继续主线程
print('end join')
def run(n):
    print('[%s]------running----\n' % n)
    time.sleep(2)
    print('--done--')


def main():
    for i in range(5):
        t = threading.Thread(target=run, args=[i,])
        t.start()
        # t.join()
        print('starting thread', t.getName())


m = threading.Thread(target=main,args=[])
# m.setDaemon(True)  # 将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不论是否执行完任务
m.start()
# m.join()    # 使主线程阻塞,直至子线程运行完毕再继续主线程
print("---main thread done----")

线程锁(互斥锁Mutex)

一个进程下能够启动多个线程,多个线程共享父进程的内存空间,也就意味着每一个线程能够访问同一份数据,此时,若是2个线程同时要修改同一份数据,会出现什么情况?

num = 100  # 设定一个共享变量

def subNum():
    global num # 在每一个线程中都获取这个全局变量
    print('--get num:', num)
    time.sleep(2)
    num -= 1 # 对此公共变量进行-1操做

thread_list = []
for i in range(100):
    t = threading.Thread(target=subNum)
    t.start()
    thread_list.append(t)

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

print('final num:', num)
# 加锁版本

def subNum():
    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=subNum)
    t.start()
    thread_list.append(t)

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

print('final num:', num)

Rlock与Lock的区别:

RLock容许在同一线程中被屡次acquire。而Lock却不容许这种状况。不然会出现死循环,程序不知道解哪一把锁。注意:若是使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁

Events

Python提供了Event对象用于线程间通讯,它是由线程设置的信号标志,若是信号标志位真,则其余线程等待直到信号接触。

Event对象实现了简单的线程通讯机制,它提供了设置信号,清除信号,等待等用于实现线程间的通讯。

event = threading.Event() 建立一个event

1 设置信号
event.set()

使用Event的set()方法能够设置Event对象内部的信号标志为真。Event对象提供了isSet()方法来判断其内部信号标志的状态。
当使用event对象的set()方法后,isSet()方法返回真

2 清除信号
event.clear()

使用Event对象的clear()方法能够清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假

3 等待
event.wait()

Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,
则wait方法一直等待到其为真时才返回。也就是说必须set新号标志位真

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('输入内容:')
if inp == 'true':
    event_obj.set()
相关文章
相关标签/搜索