进程线程.实例深入PYTHON多线程?

单线程演示:

1.线程就好比<进程线程.形象的说明进程和线程的区别?>中所说的工厂的工人,一个工人干一个任务叫做单线程

2.如下单个线程去访问4个不同的URL,要求返回URL地址和返回码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import urllib2


def get_response(url_list):
    """Get url resp code.

    :param url_list: url list
    :return:
    """
    sta_time = time.time()
    for cur_url in url_list:
        print cur_url,
        resp = urllib2.urlopen(cur_url)
        print resp.getcode()
    print 'cost time: %s' % (time.time()-sta_time)

if __name__ == '__main__':
    url_list = [
        'https://www.baidu.com/',
        'https://github.com/',
        'http://www.aliyun.com/',
        'https://www.dnspod.cn/',
    ]
    # 调用执行
    get_response(url_list)
https://www.baidu.com/ 200
https://github.com/ 200
http://www.aliyun.com/ 200
https://www.dnspod.cn/ 200
cost time: 7.49797701836

说明:如上4个URL被顺序请求,除非CPU从一个URL获得了回应,否则不会去请求下一个URL,网络请求会花费较长时间,所以CPU在等待网络请求的返回时间内会一直处于闲置状态


多线程演示:

1.线程就好比<进程线程.形象的说明进程和线程的区别?>中所说的工厂的工人,多个工人干一个任务叫做多线程

2.如下多个线程去访问4个不同的URL,要求返回URL地址和返回码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import urllib2
import threading


class MultThread(threading.Thread):
    def __init__(self, url):
        self.url = url
        super(MultThread, self).__init__()

    def run(self):
        print self.url,
        resp = urllib2.urlopen(self.url, data=None, timeout=3)
        print resp.getcode()


def get_response(url_list):
    """Get resp with mulit threads.

    :param url_list: url list
    :return:
    """
    # 创建url_list长度个线程
    sta_time = time.time()
    thread_list = []
    for cur_url in url_list:
        cur_thread = MultThread(cur_url)
        thread_list.append(cur_thread)
        # 通知CPU可以执行MultThread下面的run方法
        cur_thread.start()
    # 保证所有线程执行完毕后再执行print,否则会先执行print然后再回头继续执行线程run方法
    for cur_thread in thread_list:
        cur_thread.join()
    print 'cost time: %s' % (time.time()-sta_time)

if __name__ == '__main__':
    url_list = [
        'https://www.baidu.com/',
        'https://github.com/',
        'http://www.aliyun.com/',
        'https://www.dnspod.cn/',
    ]

    # 调用
    get_response(url_list)
https://www.baidu.com/ https://github.com/ https://www.dnspod.cn/http://www.aliyun.com/ 200
200
200
200
cost time: 1.33794403076

说明:从执行时间上面已经明显感觉有性能上提升,如上是一个多线程减少CPU等待时间,在等待一个线程内的网络请求返回时,CPU可以切换到其它线程去进行其它线程内的网络请求,所以你会发现打印的格式是先把所有的url打印出来,然后等待返回码,返回一个打印一个,我们期望一个线程处理一个url,当然也可以一个线程处理多个url,所以实例化线程类我们传入一个url,线程运行意味着执行类里的run()方法,

所以为每个url创建一个线程并调用start()方法,告诉CPU可以执行线程中的run()方法了,我们希望所有的线程都执行完毕后再计算执行时间,所以我们对每个线程调用join()方法,它会通知主线程等待这个线程结束后,才会执行下一条指令,也就是print那句,需要注意的是CPU可能不会在调用start()后马上执行run()方法,无法确定run()在不同线程间的执行顺序,对于单独的一个线程可以保证顺序执行,因为线程内的url会依次被请求并返回结果


全局变量线程安全问题:

1.在<进程线程.形象的说明进程和线程的区别?>中说,车间的空间是工人们共享的,比如许多房间里是每个工人都可以进出的,这就象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存

2.<进程线程.形象的说明进程和线程的区别?>中说,每个房间的大小不同,有些房间只能容纳一个人,比如厕所,里面有人的时候其他人不能进,这代表一个线程使用某些共享内存时,其它线程必须等待它结束,才能使用这一块儿内存

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self):
        super(MultThread, self).__init__()

    def run(self):
        global global_num
        currentnum = global_num
        print 'thread - %s currentnum - %s' % (self.name, currentnum)
        global_num = currentnum + 1


if __name__ == '__main__':
    # 全局变量
    global_num = 0

    thread_list = []
    # 开启50个线程
    for _ in xrange(50):
        cur_thread = MultThread()
        thread_list.append(cur_thread)
        cur_thread.start()
    for cur_thread in thread_list:
        cur_thread.join()

    print
    print 'global_num modify 50 times, should become 50.'
    print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0
thread - Thread-2 currentnum - 1
thread - Thread-3 currentnum - 2
thread - Thread-4 currentnum - 3
thread - Thread-5 currentnum - 4
thread - Thread-6 currentnum - 5
thread - Thread-7 currentnum - 6
thread - Thread-8 currentnum - 7
thread - Thread-9 currentnum - 8thread - Thread-10 currentnum - 8
thread - Thread-11 currentnum - 9
thread - Thread-12 currentnum - 10
thread - Thread-13 currentnum - 11

thread - Thread-14 currentnum - 9
thread - Thread-15 currentnum - 10
thread - Thread-16 currentnum - 11
thread - Thread-17 currentnum - 12
thread - Thread-18 currentnum - 13
thread - Thread-19 currentnum - 14
thread - Thread-20 currentnum - 15
thread - Thread-21 currentnum - 16
thread - Thread-22 currentnum - 17
thread - Thread-23 currentnum - 18
thread - Thread-24 currentnum - 19
thread - Thread-25 currentnum - 20
thread - Thread-26 currentnum - 21thread - Thread-27 currentnum - 21

thread - Thread-28 currentnum - 22
thread - Thread-29 currentnum - 23
thread - Thread-30 currentnum - 24
thread - Thread-31 currentnum - 25
thread - Thread-32 currentnum - 26
thread - Thread-33 currentnum - 27
thread - Thread-34 currentnum - 28
thread - Thread-35 currentnum - 29
thread - Thread-36 currentnum - 30
thread - Thread-37 currentnum - 31
thread - Thread-38 currentnum - 32
thread - Thread-39 currentnum - 33
thread - Thread-40 currentnum - 34
thread - Thread-41 currentnum - 35
thread - Thread-42 currentnum - 36
thread - Thread-43 currentnum - 37
thread - Thread-44 currentnum - 38
thread - Thread-45 currentnum - 39
thread - Thread-46 currentnum - 40
thread - Thread-47 currentnum - 41
thread - Thread-48 currentnum - 42
thread - Thread-49 currentnum - 43
thread - Thread-50 currentnum - 44

global_num modify 50 times, should become 50.
after 50 times modify, global_num is 45.

说明:有一个全局变量,所有的线程都想修改它,所有的线程应该在这个全局变量的基础上加1,有50个线程,最后这个数值应该变为50,为什么没有到50?当global_num是8的时候,Thread-10读取了global_num,这个时刻cpu将控制权给了另一个线程Thread-10,Thread-10读取到的也是8,Thread-9和Thread-10都把global_num加到9,但是我们期望的是Thread-9和Thread-10两个线程使global_num+2变为10,也就是Thread-11为10,在这里有了资源竞争,相同的情况也会发生在其它线程之间,所以出现了最后的结果小于50的情况


解决资源竞争:

1.在<进程线程.形象的说明进程和线程的区别?>中说一个防止他进入的简单方法,就是门口加一把锁,先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去,这就叫"互斥锁",防止多个线程同时读写某一块内存区域

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        super(MultThread, self).__init__()
        self.thread_lock = thread_lock

    def run(self):
        global global_num
        # 获取一把锁
        self.thread_lock.acquire()
        currentnum = global_num
        print 'thread - %s currentnum - %s' % (self.name, currentnum)
        global_num = currentnum + 1
        # 释放这把锁
        self.thread_lock.release()

if __name__ == '__main__':
    # 全局变量
    global_num = 0

    # 互斥锁
    lock = threading.Lock()

    thread_list = []
    # 开启50个线程
    for _ in xrange(50):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        cur_thread.start()
    # 等待所有线程结束再执行下面的指令
    for cur_thread in thread_list:
        cur_thread.join()

    print
    print 'global_num modify 50 times, should become 50.'
    print 'after 50 times modify, global_num is %s.' % (global_num)
thread - Thread-1 currentnum - 0
thread - Thread-2 currentnum - 1
thread - Thread-3 currentnum - 2
thread - Thread-4 currentnum - 3
thread - Thread-5 currentnum - 4
thread - Thread-6 currentnum - 5
thread - Thread-7 currentnum - 6
thread - Thread-8 currentnum - 7
thread - Thread-9 currentnum - 8
thread - Thread-10 currentnum - 9
thread - Thread-11 currentnum - 10
thread - Thread-12 currentnum - 11
thread - Thread-13 currentnum - 12
thread - Thread-14 currentnum - 13
thread - Thread-15 currentnum - 14
thread - Thread-16 currentnum - 15
thread - Thread-17 currentnum - 16
thread - Thread-18 currentnum - 17
thread - Thread-19 currentnum - 18
thread - Thread-20 currentnum - 19
thread - Thread-21 currentnum - 20
thread - Thread-22 currentnum - 21
thread - Thread-23 currentnum - 22
thread - Thread-24 currentnum - 23
thread - Thread-25 currentnum - 24
thread - Thread-26 currentnum - 25
thread - Thread-27 currentnum - 26
thread - Thread-28 currentnum - 27
thread - Thread-29 currentnum - 28
thread - Thread-30 currentnum - 29
thread - Thread-31 currentnum - 30
thread - Thread-32 currentnum - 31
thread - Thread-33 currentnum - 32
thread - Thread-34 currentnum - 33
thread - Thread-35 currentnum - 34
thread - Thread-36 currentnum - 35
thread - Thread-37 currentnum - 36
thread - Thread-38 currentnum - 37
thread - Thread-39 currentnum - 38
thread - Thread-40 currentnum - 39
thread - Thread-41 currentnum - 40
thread - Thread-42 currentnum - 41
thread - Thread-43 currentnum - 42
thread - Thread-44 currentnum - 43
thread - Thread-45 currentnum - 44
thread - Thread-46 currentnum - 45
thread - Thread-47 currentnum - 46
thread - Thread-48 currentnum - 47
thread - Thread-49 currentnum - 48
thread - Thread-50 currentnum - 49

global_num modify 50 times, should become 50.
after 50 times modify, global_num is 50.

说明:Lock用来防止竞争条件,达到我们预想的结果,如果在执行前thread-1获得了锁,其它线程在t1释放Lock之前,不会执行相同的操作,我们想要确定的是一旦Thread-1已经读取了global_num,直到完成了修改global_num,其它线程才可以读取和修改global_num


线程切换输出打断:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self):
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 强制CPU切换到其它线程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印会乱掉,但是也说明一个问题线程之间变量是相互独立的
        print self.entries

if __name__ == '__main__':
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread()
        thread_list.append(cur_thread)
        cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
, 1, 2, 3, 4, 5, 6, 7, 8, 9]

说明:上面用了一个time.sleep()来让一个线程挂起,强制切换到其它线程,但是并没有和我们期望一样打印正确结果,当一个线程正在打印时,CPU切换到另一个线程,所以产生了不正确的结果,我们需要保证print语句的原子操作,防止打印时被其它线程打断


保证线程原子操作:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        self.thread_lock = thread_lock
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 强制CPU切换到其它线程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印会乱掉,但是也说明一个问题线程之间变量是相互独立的
        self.thread_lock.acquire()
        print self.entries
        self.thread_lock.release()

if __name__ == '__main__':
    # 互斥锁
    lock = threading.Lock()
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        cur_thread.start()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

说明:这次我们看到了正确结果,同时也说明了一个问题,一个线程不可以修改其它线程内部的变量(全局变量除外)


强制线程退出:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_lock):
        self.thread_lock = thread_lock
        super(MultThread, self).__init__()

    def run(self):
        self.entries = []
        for i in xrange(10):
            # 强制CPU切换到其它线程
            time.sleep(0.01)
            self.entries.append(i)
        # 打印会乱掉,但是也说明一个问题线程之间变量是相互独立的
        self.thread_lock.acquire()
        print self.entries
        self.thread_lock.release()

if __name__ == '__main__':
    # 互斥锁
    lock = threading.Lock()
    thread_list = []
    for _ in xrange(5):
        cur_thread = MultThread(lock)
        thread_list.append(cur_thread)
        # 将主线程设置为守护线程,主线程结束后,不管子线程是否完成都一并和主线程退出
        cur_thread.setDaemon(True)
        cur_thread.start()

    # 当前线程数如果不等于1,也就是除了守护线程还有其它线程在运行
    while threading.activeCount() != 1:
        # 强制切换到守护线程外的其他线程运行
        time.sleep(0.01)
    print 'found notice: all thread is finished.'
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
found notice: all thread is finished.

说明: 可以看出Thread-[1-5]的内容打印都没有打印出来,cur_thread.setDaemon(True)的操作将父线程设置为了守护线程,根据setDaemon()方法的含义,守护线程因为不会像join()那样等待子线程结束后执行下面的语句,所以守护线程会先执行print 'found notice: all thread is finished.'然后就结束了,所有子线程也就结束了,但是为了实现join()的效果,所以在上面可以加一个判断,判断包含守护线程在内的线程数是否等于1,如果等于1则表示所有的线程已经全部结束.


允许指定线程数更改数据:

1.在<进程线程.形象的说明进程和线程的区别?>说有些房间可以容纳n个人,比如厨房,也就是说,如果人数大于n,多出来的人只能在外面等着,这好比某些内存区域,只能供给固定数目的线程使用

2.<进程线程.形象的说明进程和线程的区别?>说这时的解决办法,就是在门口挂n把锁,进去的人就取一把钥匙,出来的就把钥匙挂回原处,后来的人发现钥匙架为空,就知道必须门前排队等着,这种做法叫"信号量",用来保证多个线程不会相互冲突

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
#
# Authors: limanman
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
import time
import pprint
import threading


class MultThread(threading.Thread):
    def __init__(self, thread_semaphore):
        super(MultThread, self).__init__()
        self.thread_semaphore = thread_semaphore

    def run(self):
        # 获取锁
        self.thread_semaphore.acquire()
        # 等待1秒,开始第二波
        print 'run the thread %s' % (self.name)
        time.sleep(2)
        # 释放锁
        self.thread_semaphore.release()

if __name__ == '__main__':
    # 创建一个信号量对象,只允许同时5个线程运行,其他的线程只有其中有线程结束才能运行
    semaphore = threading.BoundedSemaphore(5)
    # 创建100个线程,但是每次只允许5个线程同时允许
    for _ in xrange(10):
        MultThread(semaphore).start()

你可能感兴趣的:(进程线程.实例深入PYTHON多线程?)