python 多线程

python 多线程 threading

一、多线程使用的的基本方式

1、使用threading中现成的方法

import threading

def f1(arg):
    print(arg)

t1 = threading.Thread(target=f1, args=(123,))
t2 = threading.Thread(target=f1, args=(456,))
t1.start()
t2.start()

说明:1、实例化一个Thead类就是生成一个线程。

     2、target是要执行的函数,args是函数的参数,以元组的形式提现。

         3、start函数代表线程已经准备好,等待CPU的调用。

 

2、使用自定义的多线程类

import threading

class MyThread(threading.Thread):
    def __init__(self, func, arg):
        self._func = func
        self._arg = arg
        super(MyThread, self).__init__()

    def run(self):
        self._func(self._arg)

t1 = MyThread(f2, 123)
t.start()
t2 = MyThread(f2, 456)
t.start()

说明:1、该类继承自threading.Thread类。

         2、自定义线程类需要重写run方法,run方法中就是该线程要做的事情。

         3、 如果需要重写构造方法__init__, 需要继承父类的构造方法调用super函数。

          4、start方法同上

 

3、守护线程

1)守护线程即守护主线程的线程,与主线程同生共死。

2)主线程退出时,守护线程也跟着退出。

3)默认情况下,创建的子线程非守护线程,既主线程如果执行完毕的,而子线程没有执行完毕,主线程会等待子线程执行完毕后再退出。

4)设置守护线程的方法是 setDaemon = True。

import threading
import time

def f1(arg):
    time.sleep(5)
    print(arg)

t = threading.Thread(target=f1, args('我是守护线程'))
t.setDaemon = True
t.start()

print('end')

说明: 该程序中子线程设置为守护线程,主线程退出后,子线程也跟着退出(没有打印‘我是守护线程’)。

 

4. join方法

join方法会是主线程阻塞,直到使用join方法的子线程执行完毕后,继续往下执行。

join方法还有个参数,设置阻塞的时间,默认情况下会无限阻塞下去。

import threading
import time

def f1(arg):
    time.sleep(5)
    print(arg)

t = threading.Thread(target=f1, args('123'))
t.setDaemon = True
t.join()

print('end')

说明:该程序会在执行到join方法的时候阻塞住,直到打印出123,再继续往下执行。

 

5. 线程中的其他方法

setName 为某个线程设置名字

getName 获取某个线程的名字

 

二、线程锁

1.Lock

由于线程使用相同的内存空间,可能在同一时间内去修改同一数据,对数据造成破坏,这种情况称作"线程不安全"。

import threading
import time

class MyThread(threading.Thread):
    def run(self):
        global num
        time.sleep(1)
        num = num+1
        msg = self.name+' set num to '+str(num)
        print msg
num = 0
def test():
    for i in range(5):
        t = MyThread()
        t.start()
if __name__ == '__main__':
    test()

但是运行结果是不正确的

 

Thread-5 set num to 2
Thread-3 set num to 3
Thread-2 set num to 5
Thread-1 set num to 5
Thread-4 set num to 4

  

问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。既“线程不安全”。

 

上面的程序引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。

线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。

 

import threading
import time

class MyThread(threading.Thread):
    def run(self):
        global num 
        time.sleep(1)

        if Lock.acquire(1):  
            num = num+1
            msg = self.name+' set num to '+str(num)
            print msg
            Lock.release()
num = 0
Lock = threading.Lock()
def test():
    for i in range(5):
        t = MyThread()
        t.start()
if __name__ == '__main__':
    test()
运行结果:

Thread-3 set num to 1
Thread-4 set num to 2
Thread-5 set num to 3
Thread-2 set num to 4
Thread-1 set num to 5

可以看出使用了线程锁的程序结果复合预期效果。

2、RLock

Lock只能对某个线程锁一次,而RLock可以锁多次。使用方法同Lock

 

3、信号量 Semaphore

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

import threading
import time
n = 10

def func(l, i):
    global n
    l.acquire()
    n -= 1
    time.sleep(1)
    print(n, i)
    l.release()

lock = threading.BoundedSemaphore(5)
for i in range(10):
    t = threading.Thread(target=func, args=(lock, i))
    t.start()
输出结果:
5 0
4 1
3 2  
2 4
2 3                          第一次(秒)输出结果:5个
-------------------------
0 5                          第二次(秒)输出结果:5个
0 6
0 7
0 8
0 9

 

4、事件 event

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

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

  • clear:将“Flag”设置为False
  • set:将“Flag”设置为True
#-*-coding:utf8-*-
import threading

def func(i,e):
    print(i)
    e.wait() # 检测是什么等,如果是红灯,停;绿灯,行
    print(i+100)

event = threading.Event()

for i in range(10):
    t = threading.Thread(target=func, args=(i,event,))
    t.start()
#========
event.clear() # 设置成红灯
inp = input('>>>')
if inp == "1":
    event.set() # 设置成绿灯
输出结果:
0
1
2
3
4
5
6
7
8
9
>>>1 # 在此处阻塞住,输入数字1,将Flag设置成True
101
100
103
105
107
109
102
106
108
104

 

5、条件Condition

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

import threading

def run(n):
    con.acquire()
    con.wait()
    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()

    while True:
        n = input('>>>')
        if inp == 'q':
            break
        con.acquire()
        con.notify(int(n))
        con.release()
输出结果如下:

>>>3
>>>run the thread: 1
run the thread: 2
run the thread: 0
3
>>>run the thread: 3
run the thread: 4
run the thread: 5
3
>>>run the thread: 7
run the thread: 8
run the thread: 6
3
>>>run the thread: 9

 

6、Timer 定时器

定时器,指定n秒后执行某操作

from threading import Timer
 
 
def hello():
    print("hello, world")
 
t = Timer(1, hello)
t.start() 1s之后启动

 

posted @ 2016-07-18 21:00  9527chu  阅读(205)  评论(0编辑  收藏  举报