您的位置:首页 > 编程语言 > Python开发

Day 07 python线程de一天

2019-08-22 17:54 113 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_43790072/article/details/100019448

Day 07 python线程de一天

多线程:

在一个进程内部,要同时干很多事,就需要同时执行多个子任务 那么我们把进程内的这些子任务叫做线程

线程的内存空间是共享的 每个线程都共享同一个进程的资源

模块: 1、_thread模块 低级模块 2、threading模块 高级模块 对_thread模块进行了封装

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  • 程序的运行速度可能加快
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
    线程在执行过程中与进程还是有区别的。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  • 线程可以被抢占(中断)。
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让。

好啦,我们开始学习Python线程

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

  • 导入threading包
  • 对象名= threading.Thread(target = 函数名) 创建线程对象
  • 对象名.start() 创建线程

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

thread.start_new_thread ( function, args[, kwargs] )

参数说明:

  • function - 线程函数。
  • args - 传递给线程函数的参数,他必须是个tuple类型。
  • kwargs - 可选参数。

Python标准库自带了两个多线程模块,分别是threading和thread,其中,thread是低级模块,threading是对thread的封装,一般,我们直接使用threading即可。

import threading

def say_hello():
print("Hello world!")

def main():
for i in range(10):
thread = threading.Thread(target=say_hello)
thread.start()

main()

输出结果是:

在这个例子中,我们首先定义了要多线程执行的函数say_hello,然后我们在主函数里创建了10个线程,target取值是say_hi,告诉线程要执行的函数,然后我们调用start()方法吩咐线程去执行这些线程。
这个程序最终会输出10个Hello world!,与[“Hello world!” for i in range(10)]效果一致,那么为什么还要使用多线程呢,我们通过下面这个例子理解下多线程的意义:

import threading
import time

def say_hello():
time.sleep(1)
print("Hello world!")

def main():
for i in range(10):
thread = threading.Thread(target=say_hello)
thread.start()

main()

在这个例子里,我们加了time.sleep(1)来模拟等待事件。现在如果用普通的循环来迭代,代码执行完需要至少20秒,而多线程运行只需要1秒多,减少了程序整体运行的时间。

给线程传参和线程常用方法
在上面的代码中,我们并没有给say_hello传参数,在多线程里传参很简单,只需要这样做就好了:

import threading
#为线程定义一个函数
def say_hello(count, name):
print("Hello world!", name)
count -= 1

def main():
name_list = ['Bob', 'Jack', 'Jone', 'Mike', 'David']
for i in range(5):
thread = threading.Thread(target=say_hello, args=(10, name_list[i]))
thread.start() #启动线程

main()

在threading.Thread类中,常用的方法有:
join:等待当前线程执行完毕

import threading
import time
def runtask(name):
print("%s线程已启动"%name)
time.sleep(2)
t = threading.Thread(target=runtask,args=("task1",))
t.start()
t.join()
print("abc")  # 过了2s才会打印,若无等待将看不到等待2s的效果

setDaemon(True):将线程设置为守护线程。若设置为守护线程,主线程结束后,子线程也将结束,并且主线程不会理会子线程是否结束,主线程不会等待子线程结束完后才结束。若没有设置为守护线程,主线程会等待子线程结束后才会结束
isAlive: 检查线程是否在运行中
getName: 获取线程名称
setName: 设置线程名称
isDaemon: 判断线程是否是守护线程
通过继承创建线程
除了直接实例化threading.Thread对象,我们还可以通过继承threading.Thread来编写多线程的类。然后 重写__init__方法和run方法 , 方法如下:

import threading
import time

class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i)
print(msg)

def test():
for i in range(5):
t = MyThread()
t.start()

if __name__ == '__main__':
test()

输出结果是:

线程与互斥锁:多个线程之间 内存是共享的,所以线程比进程轻量。多个线程是可以同时访问内存中的数据的,如果多个线程同时修改一个对象,那这份数据可能会被破坏,Python的threading类中提供了Lock方法,它会返回一个锁对象,一般通过lock.acquire()来获取锁,通过lock.release()来释放锁,对于那种只允许一个线程操作 的数据,一般把对其的操作放在lock.acquire()和lock.release()中间。
无论在什么情况下,我们都要保证代码要释放锁,所以其他语言中一般把加锁和释放锁放在try/finally语句中,这里我们可以这样使用锁:

with lock:
#lock processing

下面来看一个使用互斥锁的例子,在这个例子中,我们使用了全局变量,然后创建10个线程,每个线程做同样的事情,由于num是全局变量,而且每个线程都需要使用这个变量,因此存在着数据争用的问题,所以,我们就需要使用互斥锁保护这个全局变量:所有修改这个变量的线程在修改前都需要加锁,在increment函数中,我们通过with语句进行加锁。如下所示:

import threading

lock = threading.Lock()
num = 0

def increment(count):
global num
while count > 0:
with lock:
num += 1
count -= 1

def main():
threads = []
for i in range(10):
thread = threading.Thread(target=increment,args=(100,))
thread.start()
threads.append(thread)

for thread in threads:
thread.join()

print("except value is 1000, real value is{}".format(num))

main()

输出结果是:

如果把锁去掉那么我们永远也得不到正确的结果
对于这段代码,我们可以这样理解:

threads = []
for i in range(10):
thread = thread.Threading(target=increment, args=(100,))
thread.start()
threads.append(thread)

for thread in threads:
thread.join()

第一个for循环,意思是吩咐十个线程去做target里面的事,执行完第一个for循环后就吩咐完了,但仅仅是只是吩咐完了,target里面的任务没有执行完,因为不知道是否执行完,所以还需要创建一个列表把他们都保存起来。对于第二个for循环,如果在第一个循环里 他们的target已经执行完了,那后面直接就join,不用等待,遇到有的线程没有执行完的,join 就阻塞调用,直到这些线程执行完。
线程安全队列queue
下面是Queue类常用的方法:

  • empty: 判断队列是否为空
  • full: 判断队列是否已满 put: 向队列中添加元素
  • get: 从队列中取出元素
  • put_nowait: 非阻塞 向队列中添加元
  • get_nowait: 非阻塞 从队列中取出元素
  • join:阻塞等待,直到所有任务完成

今日总结如上,考试啦啦啦啦啦啦

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: