Python_12-线程编程

1.1    Python中的线程使用
1.1.1 函数式
1.2    创建threading.Thread的子类来包装一个线程对象
1.2.1 threading.Thread类的使用
1.3    线程优先级队列(Queue)
1.4    thread对象中的一些方法
1.4.1 join方法
1.4.2 setDaemon()方法
1.4.3 isAlive方法
1.4.4 线程名
1.5    线程同步
1.5.1 简单的线程同步
1.5.2 线程同步
1.5.3 使用条件变量保持线程同步
1.5.4 使用队列保持线程同步

1.1 Python中的线程使用

Python中使用线程有两种方式:函数或者用类来包装线程对象。

多线程类似于同时执行多个不同程序

优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

线程在执行过程中与进程还是有区别的。

l  每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。

l  线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

l  每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

l  线程可以被抢占(中断)。

l  在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。

1.1.1 函数式

调用thread模块中的start_new_thread()函数来产生新线程。如下例:

 1 import time
 2 import thread
 3 def timer(no, interval):
 4     cnt = 0
 5     while cnt<10:
 6         print ‘Thread:(%d) Time:%s/n‘%(no, time.ctime())
 7         time.sleep(interval)
 8         cnt+=1
 9     thread.exit_thread()
10
11
12 def test(): #Use thread.start_new_thread() to create 2 new threads
13     thread.start_new_thread(timer, (1,1))
14     thread.start_new_thread(timer, (2,2))
15
16 if __name__==‘__main__‘:
17     test()  

上面的例子定义了一个线程函数timer,它打印出10条时间记录后退出,每次打印的间隔由interval参数决定。thread.start_new_thread(function, args[, kwargs])的第一个参数是线程函数(本例中的timer方法),第二个参数是传递给线程函数的参数,它必须是tuple类型,kwargs是可选参数。

线程的结束可以等待线程自然结束,也可以在线程函数中调用thread.exit()或thread.exit_thread()方法。

1.2    创建threading.Thread的子类来包装一个线程对象

如下例:

 1 import threading
 2 import time
 3 class timer(threading.Thread): #The timer class is derived from the class threading.Thread
 4     def __init__(self, num, interval):
 5         threading.Thread.__init__(self)
 6         self.thread_num = num
 7         self.interval = interval
 8         self.thread_stop = False
 9
10     def run(self): #Overwrite run() method, put what you want the thread do here
11         while not self.thread_stop:
12             print( ‘Thread Object(%d), Time:%s/n‘ %(self.thread_num, time.ctime())  )
13             time.sleep(self.interval)
14     def stop(self):
15         self.thread_stop = True
16
17
18 def test():
19     thread1 = timer(1, 1)
20     thread2 = timer(2, 2)
21     thread1.start()
22     thread2.start()
23     time.sleep(10)
24     thread1.stop()
25     thread2.stop()
26     return
27
28 if __name__ == ‘__main__‘:
29     test()  

该方法创建自己的线程类,必要时重写threading.Thread类的方法,线程的控制可以由自己定制。

1.2.1 threading.Thread类的使用

1. 在自己的线程类的__init__里调用threading.Thread.__init__(self, name = threadname)

Threadname为线程的名字

2. run(),通常需要重写,编写代码实现做需要的功能。

3. getName(),获得线程对象名称

4. setName(),设置线程对象名称

5. start(),启动线程

6. jion([timeout]),等待另一线程结束后再运行。

7. setDaemon(bool),设置子线程是否随主线程一起结束,必须在start()之前调用。默认为False。

8. isDaemon(),判断线程是否随主线程一起结束。

9. isAlive(),检查线程是否在运行中。

此外threading模块本身也提供了很多方法和其他的类,可以帮助我们更好的使用和管理线程。

1.3    线程优先级队列(Queue)

Python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue。这些队列都实现了锁原语,能够在多线程中直接使用。可以使用队列来实现线程间的同步。

Queue模块中的常用方法:

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回True,反之False

Queue.full() 如果队列满了,返回True,反之False

Queue.full 与 maxsize 大小对应

Queue.get([block[, timeout]])获取队列,timeout等待时间

Queue.get_nowait() 相当Queue.get(False)

Queue.put(item) 写入队列,timeout等待时间

Queue.put_nowait(item) 相当Queue.put(item, False)

Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号

Queue.join() 实际上意味着等到队列为空,再执行别的操作

实例:

 1 #coding=utf-8
 2 import queue
 3 import threading
 4 import time
 5
 6 exitFlag = 0
 7
 8 class myThread (threading.Thread):
 9     def __init__(self, threadID, name, q):
10         threading.Thread.__init__(self)
11         self.threadID = threadID
12         self.name = name
13         self.q = q
14     def run(self):
15         print( "Starting " + self.name)
16         process_data(self.name, self.q)
17         print( "Exiting " + self.name)
18
19 def process_data(threadName, q):
20     while not exitFlag:
21         queueLock.acquire()
22         if not workQueue.empty():
23             data = q.get()
24             queueLock.release()
25             print ("%s processing %s" % (threadName, data))
26         else:
27             queueLock.release()
28         time.sleep(1)
29
30 threadList = ["Thread-1", "Thread-2", "Thread-3"]
31 nameList = ["One", "Two", "Three", "Four", "Five"]
32 queueLock = threading.Lock()
33 workQueue = queue.Queue(10)
34 threads = []
35 threadID = 1
36
37 # 创建新线程
38 for tName in threadList:
39     thread = myThread(threadID, tName, workQueue)
40     thread.start()
41     threads.append(thread)
42     threadID += 1
43
44 # 填充队列
45 queueLock.acquire()
46 for word in nameList:
47     workQueue.put(word)
48 queueLock.release()
49
50 # 等待队列清空
51 while not workQueue.empty():
52     pass
53
54 # 通知线程是时候退出
55 exitFlag = 1
56
57 # 等待所有线程完成
58 for t in threads:
59     t.join()
60 print ("Exiting Main Thread")

1.4    thread对象中的一些方法

以前说过多线程,用到threading模块中的Thread对象

start是重载了Thread对象中的run方法,其实作用还是,当执行这个start方法的时候,将运行run方法。

1.4.1 join方法

如果一个线程或者一个函数在执行过程中要调用另外一个线程,并且待到其完成以后才能接着执行,那么在调用这个线程时可以使用被调用线程的join方法。

join([timeout])

里面的参数时可选的,代表线程运行的最大时间,即如果超过这个时间,不管这个线程有没有执行完毕,主线程或函数都会接着执行的。

看个例子:

 1 #coding=utf-8
 2 import threading
 3 import time
 4 class MyThread(threading.Thread):
 5     def __init__(self,id):
 6         threading.Thread.__init__(self)
 7         self.id=id
 8     def run(self):
 9         x=0
10         time.sleep(20)
11         print self.id
12
13
14 >>> def func():
15       t.start()
16       for i in range(5):
17              print i
18
19
20 >>> t=MyThread(2)
21 >>> func()
22 0
23 1
24 2
25 3
26 4
27 >>> 2

可以看到,虽然在func里面线程已经运行,但是函数并没有等线程运行结束在执行,而是先把func执行完毕,打印0到4,然后等sleep(20),20秒结束后,这个MyThread(2),传进去的2才打印出。

 1 >>> def func():
 2       t.start()
 3       t.join()
 4       for i in range(5):
 5              print i
 6
 7
 8 >>> t=MyThread(3)
 9 >>> func()
10 3
11 0
12 1
13 2
14 3
15 4

而这个呢,是当t.start()运行开始计时,20秒后,打印出id是3,然后func才接着运行,打印出0到4.

1.4.2 setDaemon()方法

这个方法基本和join是相反的。当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法啦,

下面的例子就是设置子线程随主线程的结束而结束:

 1 import threading
 2 import time
 3
 4 class myThread(threading.Thread):
 5     def __init__(self,threadname):
 6         threading.Thread.__init__(self,name=threadname)
 7     def run(self):
 8         time.sleep(5)
 9
10         print self.getName()
11
12 def fun1():
13     t1.start()
14     print ‘fun1 done‘
15
16 def fun2():
17     t2.start()
18     print ‘fun2 done‘
19
20 t1=myThread(‘t1‘)
21 t2=myThread(‘t2‘)
22 t2.setDaemon(True)
23 fun1()
24 fun2()

上面这个例子,按照我们设想的输出时:

fun1 done

fun2 done

t1

但是实际上我们在交互模式,主线程只有在python退出时终止,所以结果t2也是被打印出来啦。

1.4.3 isAlive方法

当线程创建以后,可以使用Thread对象的isAlive方法查看线程是否运行。

 1 >>> import threading
 2 >>> import time
 3 >>> class myThread(threading.Thread):
 4        def __init__(self,id):
 5               threading.Thread.__init__(self)
 6               self.id=id
 7        def run(self):
 8               time.sleep(5)
 9               print self.id
10
11
12 >>> t=myThread(1)
13 >>> def func():
14        t.start()
15        print t.isAlive()
16
17
18 >>> func()
19 True
20 >>> 1

1.4.4 线程名

当线程创建后可以设置线程名来区分不同的线程,以便对线程进行控制。线程名可以在类的初始化函数中定义,也可以使用Thread对象的setName方法设置。下面是不同的方法来设置线程名。

 1 >>> import threading
 2 >>> class mythread(threading.Thread):
 3        def __init__(self,threadname):
 4               threading.Thread.__init__(self,name=threadname)
 5        def run(self):
 6               print self.getName()
 7
 8
 9 >>>
10 >>> t1=mythread(‘t1‘)
11 >>> t1.getName()
12 ‘t1‘
13 >>> t1.setName(‘T‘)
14 >>> t1.getName()
15 ‘T‘
16 >>> t2=mythread(‘t2‘)
17 >>> t2.start()
18 t2
19 >>>
20
21 >>> t2.getName()
22 ‘t2‘
23 >>> t2.setName(‘TT‘)
24 >>> t2.getName()
25 ‘TT‘

1.5    线程同步

1.5.1 简单的线程同步

使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:

 1 import threading
 2 import time
 3 class mythread(threading.Thread):
 4     def __init__(self,threadname):
 5         threading.Thread.__init__(self,name=threadname)
 6     def run(self):
 7         global x
 8         lock.acquire()
 9         for i in range(3):
10             x=x+1
11         time.sleep(2)
12         print x
13         lock.release()
14 lock=threading.RLock()
15 t1=[]
16 for i in range(10):
17     t=mythread(str(i))
18     t1.append(t)
19 x=0
20 for i in t1:
21 i.start()
22 运行结果如下:
23 >>> 3
24 6
25 9
26 12
27 15
28 18
29 21
30 24
31 27
32 30

而如果我们把acquire()和release()去掉,结果就不同了:

30303030303030303030

这是因为每个线程执行后在打印出x之前都要休眠2秒钟,所以在这个过程中,每个线程都被执行了,所以等到休眠结束,打印出的X的值自然就是经过多次运算以后的X的值了。

而第一次,我们把全局变量X放到了acquire()和release()之间,python解释器每次会只允许一个线程对x进行操作,只有这个线程结束对其操作并且休眠结束打印出来以后,才允许下一个线程对x操作,所以输出的X是每次递增的,而且用时间也是比较长的。

1.5.2 线程同步

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

l  使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire方法和release方法.

l  对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。如下:

多线程的优势在于可以同时运行多个任务(至少感觉起来是这样)。但是当线程需要共享数据时,可能存在数据不同步的问题。

考虑这样一种情况: 一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。

那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。

锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。

经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。

实例:

 1 #coding=utf-8
 2 import threading
 3 import time
 4
 5 class myThread (threading.Thread):
 6     def __init__(self, threadID, name, counter):
 7         threading.Thread.__init__(self)
 8         self.threadID = threadID
 9         self.name = name
10         self.counter = counter
11     def run(self):
12         print ("Starting " + self.name)
13        # 获得锁,成功获得锁定后返回True
14        # 可选的timeout参数不填时将一直阻塞直到获得锁定
15        # 否则超时后将返回False
16         threadLock.acquire()
17         print_time(self.name, self.counter, 3)
18         # 释放锁
19         threadLock.release()
20
21 def print_time(threadName, delay, counter):
22     while counter:
23         time.sleep(delay)
24         print ("%s: %s" % (threadName, time.ctime(time.time())))
25         counter -= 1
26
27 threadLock = threading.Lock()
28 threads = []
29
30 # 创建新线程
31 thread1 = myThread(1, "Thread-1", 1)
32 thread2 = myThread(2, "Thread-2", 2)
33
34 # 开启新线程
35 thread1.start()
36 thread2.start()
37
38 # 添加线程到线程列表
39 threads.append(thread1)
40 threads.append(thread2)
41
42 # 等待所有线程完成
43 for t in threads:
44     t.join()
45 print( "Exiting Main Thread")

如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。

1.5.3 使用条件变量保持线程同步

Python的Condition对象提供了对复杂线程同步的支持,使用它可以在某些事件触发之后才处理数据,condition的方法有很多,看下面的例子:

用Condition来实现著名的生产者和消费者的关系:

 1 #coding=utf-8
 2 import threading
 3 class Producer(threading.Thread):
 4     def __init__(self,threadname):
 5         threading.Thread.__init__(self,name=threadname)
 6     def run(self):
 7         global x
 8         con.acquire()
 9         if x==1000000:
10             con.wait()
11             pass
12         else:
13             for i in range(1000000):
14                 x=x+1
15             con.notify()
16         print x
17         con.release()
18
19 class Consumer(threading.Thread):
20     def __init__(self,threadname):
21         threading.Thread.__init__(self,name=threadname)
22     def run(self):
23         global x
24         con.acquire()
25         if x==0:
26             con.wait()
27             pass
28         else:
29             for i in range(1000000):
30                 x=x-1
31             con.notify()
32         print x
33         con.release()
34 con=threading.Condition()
35 x=0
36 p=Producer(‘Producer‘)
37 c=Consumer(‘Consumer‘)
38 p.start()
39 c.start()
40 p.join()
41 c.join()
42 print x

结果如下:

>>>

1000000

0

0

1.5.4 使用队列保持线程同步

同样的例子,使用Queue可以实现多生产者和多消费者的先进先出的队列,每个生产者将数据依次存入队列,而每个消费者则依次从队列中取出数据,看下面的例子:

 1 #coding=utf-8
 2 import threading
 3 import time
 4 import Queue
 5
 6 class Producer(threading.Thread):
 7     def __init__(self,threadname):
 8         threading.Thread.__init__(self,name=threadname)
 9     def run(self):
10         global queue
11         queue.put(self.getName())
12         print self.getName(),‘put‘,self.getName(),‘to queue‘
13
14 class Consumer(threading.Thread):
15     def __init__(self,threadname):
16         threading.Thread.__init__(self,name=threadname)
17     def run(self):
18         global queue
19         print self.getName(),‘get‘,self.getName(),‘from queue‘
20
21 queue=Queue.Queue()
22 plist=[]
23 clist=[]
24 for i in range(10):
25     p=Producer(‘Producer‘+str(i))
26     plist.append(p)
27 for i in range(10):
28     c=Consumer(‘Consumer‘+str(i))
29     clist.append(c)
30
31 for i in plist:
32     i.start()
33     i.join()
34 for i in clist:
35     i.start()
36     i.join()

看结果:

Producer0 put Producer0 to queue
Producer1 put Producer1 to queue
Producer2 put Producer2 to queue
Producer3 put Producer3 to queue
Producer4 put Producer4 to queue
Producer5 put Producer5 to queue
Producer6 put Producer6 to queue
Producer7 put Producer7 to queue
Producer8 put Producer8 to queue
Producer9 put Producer9 to queue
Consumer0 get Consumer0 from queue
Consumer1 get Consumer1 from queue
Consumer2 get Consumer2 from queue
Consumer3 get Consumer3 from queue
Consumer4 get Consumer4 from queue
Consumer5 get Consumer5 from queue
Consumer6 get Consumer6 from queue
Consumer7 get Consumer7 from queue
Consumer8 get Consumer8 from queue
Consumer9
时间: 2024-08-04 23:09:36

Python_12-线程编程的相关文章

Java笔试题-线程编程方面

Ja 线程编程方面 60.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用? 答:有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法 反对使用stop(),是因为它不安全.它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们.结果很难检查出真正的问题所在.suspend()方法容易发生死锁.调用suspend()的时候,

POSIX 线程编程(二)线程建立与终止

创建与终止线程 线程的管理常用的API有:pthread_create(thread,attr,start_routine,arg) pthread_exit(status) pthread_cancel(thread) pthread_attr_init(attr) pthread_attr_destroy(attr) 创建线程: 一个main程序包含一个默认的主线程,这个主线程在程序开始运行的时候由系统创建.除此之外的所有其他线程必须由程序员显式的创建. pthread_create 创建一

Posix线程编程指南(1)

Posix线程编程指南(1) 作者:杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part1/ 线程创建与取消 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第一篇将向您讲述线程的创建与取消. 1 线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有

Posix线程编程指南(5)

Posix线程编程指南(5) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part5/ 杂项 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第五篇将向您讲述pthread_self().pthread_equal()和pthread_once()等杂项函数. 在Posix线程规范中还有几个辅助函数难以归类,暂且称其为杂项函数,主要包

Posix线程编程指南(4)

Posix线程编程指南(4) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part4/ 线程终止 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第四篇将向您讲述线程中止. 线程终止方式 一般来说,Posix的线程终止有两种情况:正常终止和非正常终止.线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退

线程编程指南(Threading Programming Guide)

简介 线程是一种技术,可以在一个应用中同时执行多个代码路径.尽管新技术如操作对象和GCD提供一个更现代和更高效的工具来实现并发,OS X 和iOS也提供接口来创建和管理线程. 本文揭示了OS X中可用的线程包并展示了如何使用它们.本文还描述了应用程序中支持线程和多线程代码同步的相关技术. 重要:如果你正在开发一个新的应用,鼓励你研究实现并发的OS X技术.尤其是你不熟悉实现线程应用所需要的设计技术.这些可选择的技术简化你实现执行并发路径的工作量并提供相对传统线程更好的性能.关于这些技术的信息,参

POSIX 线程编程(一)简介

简介 在共享内存的多处理器结构中,可以用线程来实现并行.对于UNIX系统, IEEE POSIX 1003.1c标准规定了C语言线程编程接口的标准.这份标准的实现就是POSIX threads, 或者称为Pthreads. 本文开始先介绍线程的基本概念,动机和设计方面的一些考虑. 接下来是Pthreads API 的三个主要部分:线程管理,互斥锁和 条件变量.本文自始至终会贯穿大量的示例代码来展示如何使用Pthread API的每一部分. Pthreads 概述 线程是什么? 从技术角度讲,一个

Posix线程编程指南(2)

Posix线程编程指南(2) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part2/ 线程私有数据 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第二篇将向您讲述线程的私有数据. 概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程

Posix线程编程指南(3)

Posix线程编程指南(3) 杨沙洲 原文地址:http://www.ibm.com/developerworks/cn/linux/thread/posix_threadapi/part3/ 线程同步 这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第三篇将向您讲述线程同步. 1 互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thr

Linux线程编程之信号处理

前言 Linux多线程环境中的信号处理不同于进程的信号处理.一方面线程间信号处理函数的共享性使得信号处理更为复杂,另一方面普通异步信号又可转换为同步方式来简化处理. 本文首先介绍信号处理在进程中和线程间的不同,然后描述相应的线程库函数,在此基础上给出一组示例代码,以讨论线程编程中信号处理的细节和注意事项.文中涉及的代码运行环境如下: 本文通过sigwait()调用来“等待”信号,而通过signal()/sigaction()注册的信号处理函数来“捕获”信号,以体现其同步和异步的区别. 一  概念