Python 并发编程

1 为什么有操作系统

  • 操作系统: 位于底层和应用软件之间
  • 工作方式: 向下管理硬件 向上提供接口

1.1 操作系统进程切换:

  • 出现IO操作(像time.sleep之类的)
  • 固定时间(是操作系统控制的切换时间)

1.2进程的定义:

进程是一个程序一个数据集谁给你的动态执行过程
程序数据集(程序过程中使用的资源)、进程控制块(切换的时候 保存状态)

进程并发的时候,如果是一个应用程序,会把资源进行传递

进程是通过进程切换,是共享的资源,保存的的时候保存的是进程的资源集

  • 进程:
    资源管理单位(容器)
  • 线程:
    最小执行单位

1.3 并行和并发的区别:

串行是线程依次执行,运行期间独享内存
并行是每个线程占用一个内核
并发是有等待

** python的多线程:**
** 由于GIL,同一时刻,同一进程只能有一个线程被运行**

但是可以实现多进程的并发

import threading
import time

s = time.time()
def listen():
    print("listen")
    time.sleep(3)
    print("listen end")

def blog():
    print("blog")
    time.sleep(5)
    print("blog end")
if __name__ == '__main__':

	t1 = threading.Thread(target=listen)  # 实例化
	t2 = threading.Thread(target=blog)

	t1.start()  # 使用start方法调用
	t2.start()

	# t1.join()
	# t2.join()  # t1 t2 接收后 才开始主线程

	print(time.time()-s)  # 总的时间
	print("main ending")
    '''
    结果:
	listen
    blog
    0.0
    main ending    # 这个是3个运行完毕
    listen end
    blog end
    可以证明的是实现了线程的切换
    '''

使用join 方法,主线程会等待其余的结束后才运行

import threading
import time

s = time.time()
def listen():
    print("listen")
    time.sleep(3)
    print("listen end")

def blog():
    print("blog")
    time.sleep(5)
    print("blog end")
if __name__ == '__main__':

	t1 = threading.Thread(target=listen)  # 实例化
	t2 = threading.Thread(target=blog)

	t1.start()  # 使用start方法调用
	t2.start()

	t1.join()
	t2.join()  # t1 t2 接收后 才开始主线程

	print(time.time()-s)  # 总的时间
	print("main ending")

'''
结果:
listen
blog
listen end
blog end
5.001286029815674   # 这里是线程2运行结束的时间
main ending  # 最终运行主线程

'''
# 方式2  使用的是run方法

自己定义的类取执行

```python
import time
import threading


class MyThread(threading.Thread):  # 继承

    def __init__(self, num):
        threading.Thread.__init__(self)
        self.num = num

    def run(self):
        print("the num is %s" % self.num)
        time.sleep(3)

t1 = MyThread(56)  # 参数
t2 = MyThread(78)

t1.start()  
t2.start()
print("ending")  # 这是主线程
'''
结果:
the num is 56
the num is 78
ending

'''

1.4 join方法

t.join() 主线程等待对象等待完

t1.start()
t2.start()

t1.join()
t2.join()

如果是: 下面就是串行

t1.start()
t1.join()

t2.start()
t2.join()

1.5 守护线程

setDaemon

主线程结束了,子线程就结束了

线程开启之后就不能控制

有自他的子线程t1,t2的时候,t2是守护线程,主线程先等待t1,主线程结束后,守护线程关闭。

import threading
from time import ctime, sleep
import time


def Music(name):

    print("Begin listening to {name}. {time}".format(name=name, time=ctime()))
    sleep(3)
    print("end listening {time}".format(time=ctime()))


def Blog(title):

    print("Begin recording the {title}. {time}".format(
        title=title, time=ctime()))
    sleep(5)
    print('end recording {time}'.format(time=ctime()))


threads = []


t1 = threading.Thread(target=Music, args=('music',))
t2 = threading.Thread(target=Blog, args=('blog',))

threads.append(t1)
threads.append(t2)

if __name__ == '__main__':

    t1.setDaemon(True)  # 把t2设置成守护线程
    t2.setDaemon(True)
    t1.start()
    t2.start()
    # for t in threads:
    #     t.setDaemon(True) #注意:一定在start之前设置
    #     t.start()
    # for t in threads:    
    #     t.join()

    # t1.join()
    # t2.join()    #  考虑这三种join位置下的结果?

    print("all over %s" % ctime())

1.6 其他的方法

Thread实例对象的方法
  # isAlive(): 返回线程是否活动的。
  # getName(): 返回线程名。
  # setName(): 设置线程名。

threading模块提供的一些方法:
  # threading.currentThread(): 返回当前的线程变量。
  # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
  # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

GIL 全局解释器锁

Global Interpreter Lock

加在CPython的解释器中,只有CPyhton中受到限制

阻止多线程并行
** python的多线程:**
** 由于GIL,同一时刻,同一进程只能有一个线程被运行**

使用多进程的时候就可以突破这个限制

提高效率,用协程

  • 对于IO密集型的提高效率,不使用CPU
使用time.sleep的
  • 计算密集型,一直是在使用CPU,

import threading
import time

s = time.time()


def counter():
    sum = 0
    for i in range(50000000):
        sum += i

if __name__ == '__main__':
    # counter()
    # counter()
    t1 = threading.Thread(target=counter)
    t2 = threading.Thread(target=counter)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(time.time() - s)


'''
py2.7:
     串行:10.5270001888
     并发:15.7999999523
py3.:
     串行:6.322361707687378
     并发:6.371364593505859
Python3 的GIL已经进行了优化
'''

总结:
对于计算密集型的任务,Python多线程没有用
对于IO密集型的任务,Python多线程有用

Python使用多核: 开多进程
弊端: 内存开销大切换复杂

解决方案:

  • 使用协程,是单线程还可以自己控制切换的此时
  • **IO多路复用 **  以后大多的的应用场景
  • 终极思路:C模块实现多线程
原文地址:https://www.cnblogs.com/Python666/p/6825352.html