多线程编程

什么是多线程,线程是操作系统能够进行运算调度的最小单位。他包含在进程之中,是进程中的实际运作单位。线程是进程中一个单顺序的空值六,一个进程可以并发多个线程,每个线程可以并行处理不同的任务。

threading模块

python的标准库提供了两个模块用于多线程处理,_thread和threading,_thread是低级模块,threading是高级模块,是对_thread进行了封装。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
线程有两种调用方式:直接调用和继承式调用

直接调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python
# coding:utf-8
'''
Created on: 2016年5月16日
 
@author: 张晓宇
 
Email: 61411916@qq.com
 
Version: 1.0
 
Description: 多线程演示程序,直接启动方式
 
Help:
'''
def sayhi(num): # 定义每隔线程都要运行的函数
    print('%s is say hi' %num)
    import time
    time.sleep(3)
import threading
if __name__ == '__main__':
    t1 = threading.Thread(target = sayhi, args = [1, ]) # 调用Thread方法生成一个线程实例,第一个参数tartget表示进程要执行的函数,args表示要传递给进程函数的参数
    t2 = threading.Thread(target = sayhi, args = [2, ])
    t1.start() # 启动进程
    t2.start()
    t1.join() # 等待子进程完毕,这句话的意思就等待一个进程执行完在执行这句话后面的逻辑,join方法还可以接收一个超时时间参数,表示最多等待多长时间,超过这个时间就不等了,继续执行下面的语句,注意,是不等待,不是中断进程的执行
    t2.join()
    print(t1.getName()) # getName()表示获取进程的名称,默认Thread-1、Thread-2...这种命名方式
    print(t2.getName())

继承式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/env python
# coding:utf-8
'''
Created on:
 
@author: 张晓宇
 
Email: 61411916@qq.com
 
Version: 1.0
 
Description: 多线程演示程序,继承式调用
 
Help:
'''
import threading
class Mythreading(threading.Thread):
    '''
    定义一个类,继承自threading.Thread
    '''
    def __init__(self, num):
        '''
        初始化方法
        :param num:
        :return:
        '''
        threading.Thread.__init__(self)
        self.num = num
 
    def run(self):
        '''
        重写run方法,也就是每个线程要执行的函数
        :return:
        '''
        print('%s is say hi' %self.num)
        import time
        time.sleep(5)
 
if __name__ == '__main__':
    t1 = Mythreading(1) # 用刚才定义的类创建进程对象
    t2 = Mythreading(2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(t1.getName())
    print(t2.getName())

守护线程

默认情况下,线程执行完毕如果该线程下还有子线程没有执行完毕才会结束,我们可以通过守护线程的方式,是的主线程执行完毕强制结束下面的子线程的运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python3
# coding:utf-8
'''
Created on:
 
@author: 张晓宇
 
Email: 61411916@qq.com
 
Version: 1.0
 
Description: 守护线程演示程序
 
Help:
'''
import time
import threading
 
def child(n):
    '''
    子线程执行的函数
    :param n:
    :return:
    '''
    print('[%s]------running----\n' % n)
    time.sleep(2)
    print('--done--')
 
def main():
    '''
    主线程要执行的函数
    :return:
    '''
    for i in range(2): # 循环生成5个子线程
        t = threading.Thread(target=child,args=[i,])
        t.start()
        print('starting thread', t.getName())
 
 
m = threading.Thread(target=main,args=[]) # 创建主线程对象
m.setDaemon(True) #将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
m.start() # 启动主线程
m.join() # 等待主线程执行完毕
print("---main thread done----")

输出结果

1
2
3
4
5
6
7
[0]------running----
 
starting thread Thread-2
[1]------running----
 
starting thread Thread-3
---main thread done----

可以看出,main线程下的子线程执行的时间要比main线程时间长,当main线程执行完的时候,子线程的print('--done--')还没有执行就被强制结束了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env python
# coding:utf-8
'''
Created on:
 
@author: 张晓宇
 
Email: 61411916@qq.com
 
Version: 1.0
 
Description: 线程锁演示程序
 
Help:
'''
import threading
import time
def addNum():
    global num # 调用全局变量num
    print('--get num:', num)
    time.sleep(1)
    #lock.acquire()
    num += 1 # 每个线程都对num进行加1操作
    #lock.release()
    print(num)
 
if __name__ == '__main__':
    lock = threading.Lock()
    num = 0
    thread_list = [] # 初始化一个线程列表
    for i in range(10000): # 循环启动10000个进程
        t = threading.Thread(target = addNum)
        t.start()
        thread_list.append(t) # 加入到线程列表中
    for t in thread_list: # 循环等待线程列表里的所有线程结束
        t.join()
    print(num) # 打印num的最终值

正常情况,启动10000个线程,每个线程对num做加1操作,最终的结果将是10000,但是如果你用python2.X版本的解释器执行上面的代码会发现,最结果不总是10000,而且随着线程越多,这个最终结果的值稳定性越差,这就需要在对需要修改线程间共享的变量的时候加一把锁,当一个线程获得锁并操作一个共享变量的时候,其他线程只能等待锁释掉才可以,等待的过程是阻塞的,只有锁被释放掉,其他线程中“抢”到锁的进程才可以继续进行

申请锁

1
lock.acquire()

释放锁

1
lock.release()

注意,Python3.X已经修复了这个问题,不用加锁结果也是正确的

递归锁

递归锁说白了就是一个大锁里面套着小锁,也就是子锁,用到的地方不多,就不说了

信号量(Semaphore)

刚才说的锁也是互斥锁,同事只能有一个线程操作,而Semaphore可以同事允许一定数量的线程更改数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import threading,time
  
def run(n):
    semaphore.acquire()
    time.sleep(1)
    print("run the thread: %s\n" %n)
    semaphore.release()
  
if __name__ == '__main__':
  
    num= 0
    semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
    for i in range(20):
        t = threading.Thread(target=run,args=(i,))
        t.start()
  
while threading.active_count() != 1:
    pass #print threading.active_count()
else:
    print('----all threads done---')
    print(num)

事件Event

事件可以裂解为就是一个信号,他只有两个状态可以理解为真和假,常用方法如下

  • set():相当于设置为真

  • clear():相当于设置为假

  • isSet():判断是否为真

  • wait():等待事件置为真(阻塞)

​事件做常用的一个地方就是红绿灯模型,下面代码这就是演示红绿灯模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#!/usr/bin/env python
# coding:utf-8
'''
Created on: 2016年3月5日
 
@author: 张晓宇
 
Email: 61411916@qq.com
 
Version: 1.0
 
Description: 红绿灯模型演示程序
 
Help:
'''
import threading
def light():
    '''
    信号灯进程的线程执行的函数
    :return:
    '''
    import time
    if not event.isSet(): # 判断是否为真,如果不为真就设置为真,也就是一上来是绿灯
        event.set()
    count = 0 # 初始化计数器,可以理解为红绿灯之间的等待时间
    while True: # 无限制循环下去
        if count < 10:
            # 0-10秒是绿灯
            print('\033[42;1m--green light on--\033[0m')
        elif count < 13:
            # 10-12是黄灯
            print('\033[43;1m--yellow light on--\033[0m')
        elif count < 20:
            # 14-19秒是红灯
            if event.isSet(): # 判断如果为真,就设置成假的
                event.clear()
            print('\033[41;1m--red light on--\033[0m')
        else:
            # 第20秒的时候计数器归零,并从新设置为真
            count = 0
            if not event.isSet():
                event.set()
        time.sleep(1)
        count += 1 # 计数器加1
 
def car(n):
    '''
    汽车进程要执行的函数
    :param n:
    :return:
    '''
    import time
    while True:
        time.sleep(1) # 每隔1秒检查一下红绿灯状态
        if event.isSet():
            # 如果为真就runing
            print('car [%s] is running...' %n)
        else:
            # 否则就waiting
            print('car [%s] is waiting for the red light...' %n)
            event.wait() # 等待事件变为真
 
if __name__ == '__main__':
    event = threading.Event() # 创建Event对象
    Light = threading.Thread(target = light) # 创建信号灯线程
    Light.start() # 启动线程
    for i in range(3): # 循环创建3个汽车线程对象,并启动
        t = threading.Thread(target = car, args = [i,])
        t.start()

我的Python成长之路---第八天---Python基础(24)---2016年3月5日(晴)的更多相关文章

  1. 我的Python成长之路---第八天---Python基础(25)---2016年3月5日(晴)

    多进程 multiprocessing模块 multiprocessing模块提供了一个Process类来代表一个进程对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  2. 我的Python成长之路---第八天---Python基础(23)---2016年3月5日(晴)

    socketserver 之前讲道德socket模块是单进程的,只能接受一个客户端的连接和请求,只有当该客户端断开的之后才能再接受来自其他客户端的连接和请求.当然我们也可以通过python的多线程等模 ...

  3. 我的Python成长之路---第一天---Python基础(1)---2015年12月26日(雾霾)

    2015年12月26日是个特别的日子,我的Python成之路迈出第一步.见到了心目中的Python大神(Alex),也认识到了新的志向相投的伙伴,非常开心. 尽管之前看过一些Python的视频.书,算 ...

  4. 我的Python成长之路---第二天---Python基础(7)---2016年1月9日(晴)

    再说字符串 一.字符串的编码 字符串的编码是个很令人头疼的问题,由于计算机是美国人发明的,他们很理所当然的认为计算机只要能处理127个字母和一些符号就够用了,所以规定了一个字符占用8个比特(bit)也 ...

  5. python成长之路——第八天

    pickle,load :切记:如果load的是个对象的话,必须导入构建这个对象的类     封装 类和对象的关系: 每个对象里都有一个类对象指针,指向类     继承:支持单继承和多继承 print ...

  6. 我的Python成长之路---第一天---Python基础(6)---2015年12月26日(雾霾)

    七.列表——list Python的列表是一种内置的数据类型,是由Python的基本数据类型组成的有序的集合.有点类似C语言的数组,但与数组不同的是,Python在定义列表的时候不用指定列表的容积(长 ...

  7. 我的Python成长之路---第一天---Python基础(5)---2015年12月26日(雾霾)

    六.流程控制 与C语言不通的事Python的流程控制的代码块不是用{}花括号表示的,而是用强制缩进来,而且缩进必须一致,官方推荐是使用4个空格,不建议使用使用tab(制表符)做缩进,一是不同的系统ta ...

  8. 我的Python成长之路---第一天---Python基础(3)---2015年12月26日(雾霾)

    四.变量和常量 变量是用来存储程序运行期间需要临时保存可以不断改变的数据的标识符.Python有自身的内存回收机制,所以在开发过程中不用考虑变量的销毁等 Python中的变量名命名有如下规则: 1.变 ...

  9. 我的Python成长之路---第一天---Python基础(2)---2015年12月26日(雾霾)

    三.数据类型 Python基本类型(能够直接处理的数据类型有以下几种)主要有5种 1.整数(int) Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如 ...

随机推荐

  1. Microsoft Azure 负载平衡服务

     Microsoft Azure 为在其中托管的虚拟机(IaaS) 和云服务(PaaS) 提供负载平衡服务.负载平衡支持应用程序伸缩,并且提供应用程序故障恢复以及其他优势. 可以通过以下方式访问负 ...

  2. QT与JavaScript互调 - 虹的日志 - 网易博客

    QT与JavaScript互调 - 虹的日志 - 网易博客 QT与JavaScript互调   2012-05-29 21:43:14|  分类: 技术 |  标签:qt  javascript  w ...

  3. 一步一步学android之控件篇——ScrollView

    一个手机的屏幕大小是有限的,那么我要显示的东西显示不下怎么办?这就会使用到ScrollView来进行滚动显示,他的定义如下: 可以看到ScrollView是继承于FrameLayout的,所以Scro ...

  4. node.weiChat

    微信的朋友圈分享是现在流行的推广模式,最近两天尝试了一下使用微信进行商品的分享,分享结束后我可以在自己的数据库中查询到用户是否分享成功,包括用户使用微信进行支付时的成功验证.个人觉得微信上的教程有些绕 ...

  5. 使用LAMP创建基于wordpress的个从博客站点

    參考: http://blog.csdn.net/ck_boss/article/details/27866117 一.mysql配置 1.安装mysql yum install mysql-serv ...

  6. linux网络编程涉及的函数

    常用的网络命令: netstat 命令netstat是用来显示网络的连接,路由表和接口统计等网络的信息. netstat有许多的选项我们常用的选项是-an用来显示详细的网络状态.至于其它选项我们使用帮 ...

  7. log4net日志的配置及简单应用

    在程序运行中,往往会出现各种出乎开发人员意料的异常或者错误,所以,记录详细的程序运行日志信息,有利于开发人员和运维人员排查异常信息,提高工作效率.而本菜鸟在大神推荐和指导下使用log4net这一插件工 ...

  8. HDU 4861 Couple doubi(找规律|费马定理)

    Couple doubi Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit ...

  9. (转)原子操作 Interlocked系列函数

    上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...

  10. BZOJ 1485: [HNOI2009]有趣的数列( catalan数 )

    打个表找一下规律可以发现...就是卡特兰数...卡特兰数可以用组合数计算.对于这道题,ans(n) = C(n, 2n) / (n+1) , 分解质因数去算就可以了... -------------- ...