Python中的线程创建

Python中的线程


一.创建线程

线程:CPU的基本调度单位,是程序执行流的最小单元,线程有就绪,阻塞和运行三种状态,就绪就是指线程具备所有的运行条件,逻辑上可以运行,只需等待CPU分配时间片即可,运行状态就是指线程占有CPU时间片,正在运行,阻塞是指线程在等待一个事件(如某个信号量),逻辑上不可执行,每一个程序至少有一个线程。线程是程序中一个单一的顺讯控制流程,进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的调度单位,在单个程序中同时运行多个线程完成不同的工作,称为多线程。


import  _thread              #较为底层的线程创建方式
import  threading		  #较常用

import time
def job():
    print("这是一个需要执行的任务")
    # 激活的线程个数
    print("当前线程的个数:", threading.active_count())
    # 打印当前线程的详细信息
    print("当前线程信息:", threading.current_thread())
    time.sleep(100)

if __name__ == "__main__":
    job()

在这里插入图片描述

二.创建多线程

1._thread方式创建多线程


import _thread

import time
def job(name):
    print("这是一个需要执行的任务")
    # # 激活的线程个数
    # print("当前线程的个数:", threading.active_count())
    # # 打印当前线程的详细信息
    # print("当前线程信息:", threading.current_thread())
    print(name, time.ctime())
    time.sleep(2)
if __name__ == "__main__":
    # 创建多个线程, 但是没有开始执行任务;
    _thread.start_new_thread(job,('thread1', ))
    _thread.start_new_thread(job,('thread2', ))
    while True:
        pass

在这里插入图片描述

2.threading方式创建多线程

import threading

import time

def job(name):
    print("这是一个需要执行的任务: %s" %(name))
    # 激活的线程个数
    print("当前线程的个数:", threading.active_count())
    # 打印当前线程的详细信息
    print("当前线程信息:", threading.current_thread())
    time.sleep(100)

    print(name, time.ctime())

if __name__ == "__main__":
    job('job0')
    # 创建多个线程
    t1 = threading.Thread(target=job, name='job1', args=("job1-name",))
    t1.start()
    t2 = threading.Thread(target=job, name='job2', args=("job2-name",))
    t2.start()
    print('hello')

在这里插入图片描述

三.多线程的JOIN方法

join方法是使程序等待所有的子线程执行结束之后, 继续执行主线程的内容,即等待,直到t1线程执行结束;其间阻塞正在调用的线程。

import threading
import time

def music(name):
    for i in range(2):
        print("正在听音乐%s" %(name))
        time.sleep(1)

def code(name):
    for i in range(2):
        print("正在编写代码%s" %(name))
        time.sleep(2)

if __name__ == '__main__':
    start_time = time.time()
    t1 = threading.Thread(target=music, args=("Light Dance",))
    t2 = threading.Thread(target=code, args=("多线程", ))

    t1.start()
    t2.start()

    # 等待所有的子线程执行结束之后, 继续执行主线程的内容;
    t1.join()
    t2.join()

    print("花费时间: %s" %(time.time()-start_time))

在这里插入图片描述

四.threading的set_daemon方法

# 当主线程执行结束, 让没有执行的线程强制结束;set_daemon
import threading
import time

# 任务1:
def music(name):
    for i in range(2):
        print("正在听音乐%s" %(name))
        time.sleep(1)
# 任务2:
def code(name):
    for i in range(2):
        print("正在编写代码%s" %(name))
        time.sleep(2)

if __name__ == '__main__':
    start_time = time.time()

    t1 = threading.Thread(target=music, args=("Light Dance",))
    t2 = threading.Thread(target=code, args=("多线程", ))

    # 将t1线程生命为守护线程, 如果设置为True, 子线程启动, 当主线程执行结束, 子线程也结束
    # 设置setDaemon必须在启动线程之前进行设置;
    t1.setDaemon(True)
    t2.setDaemon(True)
    t1.start()
    t2.start()
    print("花费时间: %s" %(time.time()-start_time))

在这里插入图片描述

五.继承的方法创建多线程

1.任务无需任何参数

import  threading
# 类的继承
class IpThread(threading.Thread):
    # 重写构造方法;
    def __init__(self, jobname):
        super(IpThread, self).__init__()
        self.jobname = jobname

    # 将多线程需要执行的任务重写到run方法中;
    def run(self):
        print("this is  a job")

t1 = IpThread(jobname="new job")
t1.start()

在这里插入图片描述

2.任务需要参数

import json
import threading
# 类的继承
from urllib.error import HTTPError
from urllib.request import urlopen

import time

class IpThread(threading.Thread):
    # 重写构造方法;如果执行的任务需要传递参数, 那将参数通过构造函数与self绑定;
    def __init__(self, jobname, ip):
        super(IpThread, self).__init__()
        self.jobname = jobname
        self.ip = ip

        # 将多线程需要执行的任务重写到run方法中;
    def run(self):
        try:
            # 需要有一个参数, 传ip;
            url = "http://ip.taobao.com/service/getIpInfo.php?ip=%s" % (self.ip)
            # 根据url获取网页的内容, 并且解码为utf-8格式, 识别中文;
            text = urlopen(url).read().decode('utf-8')
        except HTTPError as e:
            print("Error: %s获取地理位置网络错误" %(self.ip))
        else:
            # 将获取的字符串类型转换为字典, 方便处理
            d = json.loads(text)['data']
            country = d['country']
            city = d['city']
            print("%s:" % (self.ip), country, city)
def use_thread():
    start_time = time.time()
    ips = ['172.25.254.250', '8.8.8.8',
           '172.25.254.250', '8.8.8.8',
           '172.25.254.250', '8.8.8.8']
    threads = []
    for ip in ips:
        t = IpThread(jobname="爬虫", ip=ip)
        threads.append(t)
        t.start()
    # 等待所有的子线程执行结束
    [thread.join() for thread in threads]
    print("Success, 运行时间为%s" % (time.time() - start_time))

if __name__ == "__main__":
    use_thread()

在这里插入图片描述

六.线程锁

多个线程对同一个数据进行修改时, 可能出现不可预料的情况,所以需要使用线程锁使得数据的正确性得到保证。

不加锁之前:

import threading

def add(lock):
    # 2. 操作变量之前进行加锁
    global money
    for i in range(1000000):
        money += 1
    # 3. 操作变量完成后进行解锁


def reduce(lock):
    # 2. 操作变量之前进行加锁

    global  money
    for i in range(1000000):
        money -= 1
    # 3. 操作变量完成后进行解锁

if __name__ == '__main__':
    money = 0
    # 1. 实例化一个锁对象
    lock = threading.Lock()
    t1 = threading.Thread(target=add, args=(lock, ))
    t2 = threading.Thread(target=reduce, args=(lock, ))
    t1.start()
    t2.start()
    # 等待所有子线程执行结束
    t1.join()
    t2.join()

    print("最终金额为:%s" %(money))

在这里插入图片描述
加锁之后:

import threading

def add(lock):
    # 2. 操作变量之前进行加锁
    lock.acquire()
    global money
    for i in range(1000000):
        money += 1
    # 3. 操作变量完成后进行解锁
    lock.release()

def reduce(lock):
    # 2. 操作变量之前进行加锁
    lock.acquire()
    global  money
    for i in range(1000000):
        money -= 1
    # 3. 操作变量完成后进行解锁
    lock.release()

if __name__ == '__main__':
    money = 0
    # 1. 实例化一个锁对象
    lock = threading.Lock()
    t1 = threading.Thread(target=add, args=(lock, ))
    t2 = threading.Thread(target=reduce, args=(lock, ))
    t1.start()
    t2.start()
    # 等待所有子线程执行结束
    t1.join()
    t2.join()

    print("最终金额为:%s" %(money))

在这里插入图片描述

七.Python中的GIL全局解释器锁

  • GIL(全局解释器锁)
  • python解释器默认每次只允许一个线程执行
    执行过程:
    1). 设置GIL
    2). 切换到线程去运行对应的任务;
    3). 运行
    - 执行完了
    - time.sleep()
    - 获取其他信息才能继续执行, eg: 从网络上获取网页信息等;
    4). 把线程设置为睡眠状态
    5). 解锁GIL
    6). 再次重复执行上述内容

方法的选择:
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,
使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,
它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,
但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,
使用threading包并不是一个很好的方法。

import threading
from mytimeit import timeit
def job(l):
    sum(l)

@timeit
def use_thread():
    li = range(1,10000)
    for i in range(5):
        t = threading.Thread(target=job, args=(li, ))
        t.start()
@timeit
def use_no_thread():
    li = range(1, 10000)
    for i in range(5):
        job(li)


if __name__ == "__main__":
    use_thread()
    use_no_thread()


在这里插入图片描述

八.线程池

from concurrent.futures import  ThreadPoolExecutor		   #导入线程池,注意: python3.2版本以后才可以使用
import time

# 需要执行的任务
def job():
    print("this is a job")
    return  "hello"

if __name__ == '__main__':
    # 实例化对象, 线程池包含10个线程来处理任务;
    pool = ThreadPoolExecutor(max_workers=10)

    # 往线程池里面扔需要执行的任务, 返回一个对象,( _base.Future实例化出来的)
    f1 = pool.submit(job)
    f2 = pool.submit(job)

    # 判断任务是否执行结束
    print(f1.done())
    time.sleep(1)
    print(f2.done())

    # 获取任务执行的结果
    print(f1.result())
    print(f2.result())

九.线程池中的submit提交与map方法

from urllib.error import HTTPError
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
import time
URLS = ['http://httpbin.org', 'http://example.com/',
        'https://api.github.com/'] * 100
def get_page(url, timeout=3):
    try:
        content = urlopen(url).read()
        return {'url':url, 'len':len(content)}
    except HTTPError as e:
        return {'url':url, 'len':0}


# 方法1: submit提交任务
start_time = time.time()
pool = ThreadPoolExecutor(max_workers=20)
futuresObj = [pool.submit(get_page, url) for url in URLS]

# 注意: 传递的时包含futures对象的序列, as_complete, 返回已经执行完任务的future对象,
# 直到所有的future对应的任务执行完成, 循环结束;
# for finish_fs in as_completed(futuresObj):
#     print(finish_fs.result() )

for future in futuresObj:
    print(future.result())

print("submit执行时间:%s" %(time.time()-start_time))
#

# 方法2:通过map方式执行
start_time = time.time()
pool = ThreadPoolExecutor(max_workers=20)
for res in pool.map(get_page, URLS):
    print(res)
print("map执行时间:%s" %(time.time()-start_time))

在这里插入图片描述
在这里插入图片描述
综上,map处理的稍快一些,而且网速不是很稳定,但考虑到map更加简洁,推荐使用map处理。

猜你喜欢

转载自blog.csdn.net/weixin_41179709/article/details/82843037
今日推荐