首页 > 代码库 > Python学习——Python线程

Python学习——Python线程

一、线程创建

 1 #方法一:将要执行的方法作为参数传给Thread的构造方法
 2 import threading
 3 import time
 4 
 5 def show(arg):
 6     time.sleep(2)
 7     print(thread + str(arg))
 8 
 9 for i in range(10):
10     t = threading.Thread(target=show,args=(i,))
11     time.sleep(2)
12     t.start()
13 
14 #方法2:从Thread继承,并重写run()
15 class MyThread(threading.Thread):
16     def __init__(self,num):
17         threading.Thread.__init__(self)
18         self.num = num
19 
20     def run(self)):#定义每个线程要运行的函数
21         print("running on number:%s" %self.num)
22         time.sleep(3)
23 
24 
25 if __name__ ==  __main__:
26     t1 = MyThread(1)
27     t2 = MyThread(2)
28     t1.start()
29     time.sleep(3)
30     t2.start()

注解:
Thread(group=None,target=None,name=None,args=(),kwargs={})
group:线程组,目前还没有实现,库引用时提示必须是None
target:要执行的方法
name:线程名
args/kwargs:要传入方法的参数,args和kwargs两个参数其实是二选一
#实例方法
isAlive():返回线程是否在运行
get/setName(name):获取/设置线程名
is/setDaemon(bool):获取/设置是否守护线程。初始值从创建该线程的线程继承,当没有非守护线程仍在运行时,程序将终止
start():启动线程
join([timeout]):阻塞当前上下文环境的线程。

二、Python多线程用法

 1 import threading
 2 from time import ctime,sleep
 3 
 4 def music(func):
 5     for i in range(2):
 6         print("I was listening to %s. %s" %(func,ctime()))
 7         sleep(1)
 8 def move(func):
 9     for i in range(2):
10         print("I was at the %s! %s" %(func,ctime()))
11         sleep(5)
12 
13 threads = []
14 t1 = threading.Thread(target=music,args=(童话镇,))
15 threads.append(t1)
16 t2 = threading.Thread(target=move,args=(变形金刚,))
17 threads.append(t2)
18 
19 if __name__ == __main__:
20     for t in threads:
21         t.setDaemon(True)
22         t.start()
23 
24     print("all over %s" %ctime())

注:

threads = []

t1 = threading.Thread(target=music,args=(‘童话镇‘,))

threads.append(t1)

  创建了threads数组,创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=music,args方法对music进行传参。 把创建好的线程t1装到threads数组中。

  接着以同样的方式创建线程t2,并把t2也装到threads数组。

for t in threads:

  t.setDaemon(True)

  t.start()

最后通过for循环遍历数组。(数组被装载了t1和t2两个线程)

setDaemon()

  setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print "all over %s" %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。

       serDeamon(False)(默认)前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,主线程停止。

运行结果:

I was listening to 童话镇. Thu Jun 22 23:23:07 2017
I was at the 变形金刚! Thu Jun 22 23:23:07 2017
all over Thu Jun 22 23:23:07 2017

         从执行结果来看,子线程(muisc 、move )和主线程(print "all over %s" %ctime())都是同一时间启动,但由于主线程执行完结束,所以导致子线程也终止。 

调整程序:

 

1 if __name__ == __main__:
2     for t in threads:
3         t.setDaemon(True)
4         t.start()
5     
6     t.join()
7 
8     print "all over %s" %ctime()

         加了join()方法,用于等待线程终止。join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

         join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。

运行结果:

1 ############运行结果###################
2 I was listening to 童话镇. Thu Jun 22 23:34:22 2017
3 I was at the 变形金刚! Thu Jun 22 23:34:22 2017
4 I was listening to 童话镇. Thu Jun 22 23:34:23 2017
5 I was at the 变形金刚! Thu Jun 22 23:34:27 2017
6 all over Thu Jun 22 23:34:32 2017

从结果的时间可以看出每首歌之间等待1秒,电影之间等待5秒,但是是同步进行的,总的时间为5秒

三、线程锁(LOCK,RLOCK)
         由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻允许一个线程执行操作。

  Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

  可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。  

  RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

  可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

  简言之:Lock属于全局,Rlock属于线程。

1,未使用锁

 1 import threading
 2 import time
 3 
 4 num = 0
 5 
 6 def show(arg):
 7     global num
 8     time.sleep(1)
 9     num +=1
10     print(num)
11 
12 for i in range(10):
13     t = threading.Thread(target=show, args=(i,))
14     t.start()
15 
16 print(main thread stop)

多次运行可能产生混乱。这种场景就是适合使用锁的场景。

2.使用锁

 1 import threading
 2 import time
 3 
 4 num = 0
 5 lock = threading.RLock()
 6 
 7 # 调用acquire([timeout])时,线程将一直阻塞,
 8 # 直到获得锁定或者直到timeout秒后(timeout参数可选)。
 9 # 返回是否获得锁。
10 def show(arg):
11     lock.acquire()
12     global num
13     time.sleep(1)
14     num +=1
15     print(num)
16     lock.release()
17 
18 for i in range(10):
19     t = threading.Thread(target=show, args=(i,))
20     t.start()
21 
22 print(main thread stop)

加上锁后数字会一步步打印出来,不会因为拥堵而错乱的情况!


四、信号量(Semaphore)

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

 1 import threading, time
 2 
 3 def run(n):
 4     semaphore.acquire()
 5     time.sleep(3)
 6     print("run the thread: %s" % n)
 7     semaphore.release()
 8 
 9 if __name__ == __main__:
10     num = 0
11     semaphore = threading.BoundedSemaphore(5)  # 最多允许5个线程同时运行
12     for i in range(20):
13         t = threading.Thread(target=run, args=(i,))
14         t.start()

 五、事件(event)

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

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

clear:将“Flag”设置为False

set:将“Flag”设置为True

用threading.Event实现线程间的通讯

threading.Event使一个线程等待其他线程的通知,把这个Event传递到线程对象中,Event默认内置了一个标志,初始值为False。
一旦该线程通过wait()方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时,
该Event会通知所有等待状态的线程恢复运行。

 1 import threading
 2 
 3 def do(event):
 4     print(start)
 5     event.wait()
 6     print(end)
 7 
 8 event_obj = threading.Event()
 9 
10 for i in range(10):
11     t = threading.Thread(target=do, args=(event_obj,))
12     t.start()
13 
14 event_obj.clear() #继续阻塞
15 
16 inp = input(input:)
17 if inp == true:
18     event_obj.set()  # 唤醒

六、条件(condition)
所谓条件变量,即这种机制是在满足特定条件之后,线程才可以访问相关的数据!

它使用Condition类来完成,由于它也可以像锁机制那样用,所以它也有acquire方法和release方法,而且它还有wait,notify,notifyAll方法

一个简单的生产消费者模型,通过条件变量的控制产品数量的增减,调用一次生产者产品就是+1,调用一次消费者产品就会-1.
使用 Condition 类来完成,由于它也可以像锁机制那样用,所以它也有 acquire 方法和 release 方法,而且它还有
wait, notify, notifyAll 方法。
 1 import threading
 2 import  time
 3 
 4 # 产品类
 5 class Goods:
 6     def __init__(self):
 7         self.count = 0
 8 
 9     def add(self, num=1):
10         self.count += num
11 
12     def sub(self):
13         if self.count >= 0:
14             self.count -= 1
15 
16     def empty(self):
17         return self.count <= 0
18 
19 # 生产者
20 class Producer(threading.Thread):
21     def __init__(self, condition, goods, sleeptime=1):
22         threading.Thread.__init__(self)
23         self.cond = condition
24         self.goods = goods
25         self.sleeptime = sleeptime
26 
27     def run(self):
28         cond = self.cond
29         goods = self.goods
30         while True:
31             # 锁住资源
32             cond.acquire()
33             goods.add()
34             print("产品数量:", goods.count, "生产者线程")
35             # 唤醒所有等待的线程 -> 其实就是唤醒消费者进程
36             cond.notifyAll()
37             # 解锁资源
38             cond.release()
39             time.sleep(self.sleeptime)
40 
41 
42 # 消费者
43 class Consumer(threading.Thread):
44     def __init__(self, condition, goods, sleeptime=2):
45         threading.Thread.__init__(self)
46         self.cond = condition
47         self.goods = goods
48         self.sleeptime = sleeptime
49 
50     def run(self):
51         cond = self.cond
52         goods = self.goods
53 
54         while True:
55             time.sleep(self.sleeptime)
56             # 锁住资源
57             cond.acquire()
58             # 如无产品则让线程等待
59             while goods.empty():
60                 cond.wait()
61             goods.sub()
62             print("产品数量:", goods.count, "消费者线程")
63             
64 
65 g = Goods()
66 c = threading.Condition()
67 pro = Producer(c, g)
68 pro.start()
69 con = Consumer(c, g)
70 con.start()

七、定时器(Timer)

 1 import threading
 2 def SayHello():
 3     print("hello world!")
 4     t=threading.Timer(3,SayHello)
 5     t.start()
 6 def other_func():
 7     print("let me running!")
 8     t=threading.Timer(1,other_func)
 9     t.start()
10 
11 if __name__ == "__main__":
12     SayHello()
13     other_func()

 

 

 

 

 

 

 

 

 

 

Python学习——Python线程