多线程与并行

线程与进程的基本概念的介绍

进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。我们自己在python文件中写了一些代码,这叫做程序,运行这个python文件的时候,这叫做进程。

线程

线程(Thread)是操作系统能够进行运算调度的最小单位,他被包含在进程之中,是进程中的实际运作单位

多线程的基本概念

多线程是指在软件或硬件上实现多个线程并发执行的技术。具有多线程的计算机因有硬件支持而能够在同一时间执行多个线程。软件多线程是说即便处理器只能运行一个线程,操作系统也可以快速地在不同线程间切换,由于时间间隔小,给用户造成一种多个线程在同时运行的假象,这样的运行机制被称为软件多线程。

全局解释器锁

全局解释器锁(Global Interpreter Lock)是计算机程序设计语言解释器用于同步线程的工具,使得在同一进程内任何时刻仅有一个线程在执行。常见例子有CPython(JPython不使用GIL)与Ruby MRI。(主要用于解决CPython的内存管理不是线程安全的)。

Python线程模块

Python标准库中关于线程的主要是_thread和threading模块

_thread模块

标准库中的_thread模块作为低级别的模块存在,一般不建议直接使用,所以不做介绍。

threading模块

threading模块不仅提供了面向对象的线程实现方式,还提供了各种有用的对象和方法方便我们创建和控制线程,使用threading模块创建线程很方便,大部分操作都是围绕threading.Thread类来实现的。
接下来举一个简单地例子

import threading
import time


def run(name):
    print(name,"线程执行了")
    time.sleep(5)

#程序执行时,本身就是一个线程,叫主线程
#手动创建的是子线程
#主线程的执行中不会等待子线程执行完毕就会直接执行后面的代码
# 创建线程
t1 = threading.Thread(target=run, args=("t1",))
t2 = threading.Thread(target=run, args=("t2",))
t1.start()
t2.start()
t1.join()#设置子线程执行完毕再去执行主线程
t2.join()
print("执行完毕")

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/haha.py
A窗口 卖出一张票,还剩 99 张
B窗口 卖出一张票,还剩 98 张
A窗口 卖出一张票,还剩 97 张
B窗口 卖出一张票,还剩 96 张
A窗口 卖出一张票,还剩 95 张
B窗口 卖出一张票,还剩 94 张
A窗口 卖出一张票,还剩 93 张
B窗口 卖出一张票,还剩 92 张
A窗口 卖出一张票,还剩 91 张
B窗口 卖出一张票,还剩 90 张
A窗口 卖出一张票,还剩 89 张
B窗口 卖出一张票,还剩 88 张
A窗口 卖出一张票,还剩 87 张
B窗口 卖出一张票,还剩 86 张
A窗口 卖出一张票,还剩 85 张
B窗口 卖出一张票,还剩 84 张
A窗口 卖出一张票,还剩 83 张
B窗口 卖出一张票,还剩 82 张
A窗口 卖出一张票,还剩 81 张
B窗口 卖出一张票,还剩 80 张
A窗口 卖出一张票,还剩 79 张
B窗口 卖出一张票,还剩 78 张
A窗口 卖出一张票,还剩 77 张
B窗口 卖出一张票,还剩 76 张
A窗口 卖出一张票,还剩 75 张
B窗口 卖出一张票,还剩 74 张
A窗口 卖出一张票,还剩 73 张
B窗口 卖出一张票,还剩 72 张
A窗口 卖出一张票,还剩 71 张
B窗口 卖出一张票,还剩 70 张
A窗口 卖出一张票,还剩 69 张
B窗口 卖出一张票,还剩 68 张
A窗口 卖出一张票,还剩 67 张
B窗口 卖出一张票,还剩 66 张
A窗口 卖出一张票,还剩 65 张
B窗口 卖出一张票,还剩 64 张
A窗口 卖出一张票,还剩 63 张
B窗口 卖出一张票,还剩 62 张
A窗口 卖出一张票,还剩 61 张
B窗口 卖出一张票,还剩 60 张
A窗口 卖出一张票,还剩 59 张
B窗口 卖出一张票,还剩 58 张
A窗口 卖出一张票,还剩 57 张
B窗口 卖出一张票,还剩 56 张
A窗口 卖出一张票,还剩 55 张
B窗口 卖出一张票,还剩 54 张
A窗口 卖出一张票,还剩 53 张
B窗口 卖出一张票,还剩 52 张
A窗口 卖出一张票,还剩 51 张
B窗口 卖出一张票,还剩 50 张
A窗口 卖出一张票,还剩 49 张
B窗口 卖出一张票,还剩 48 张
A窗口 卖出一张票,还剩 47 张
B窗口 卖出一张票,还剩 46 张
A窗口 卖出一张票,还剩 45 张
B窗口 卖出一张票,还剩 44 张
A窗口 卖出一张票,还剩 43 张
B窗口 卖出一张票,还剩 42 张
A窗口 卖出一张票,还剩 41 张
B窗口 卖出一张票,还剩 40 张
A窗口 卖出一张票,还剩 39 张
B窗口 卖出一张票,还剩 38 张
A窗口 卖出一张票,还剩 37 张
B窗口 卖出一张票,还剩 36 张
A窗口 卖出一张票,还剩 35 张
B窗口 卖出一张票,还剩 34 张
A窗口 卖出一张票,还剩 33 张
B窗口 卖出一张票,还剩 32 张
A窗口 卖出一张票,还剩 31 张
B窗口 卖出一张票,还剩 30 张
A窗口 卖出一张票,还剩 29 张
B窗口 卖出一张票,还剩 28 张
A窗口 卖出一张票,还剩 27 张
B窗口 卖出一张票,还剩 26 张
A窗口 卖出一张票,还剩 25 张
B窗口 卖出一张票,还剩 24 张
A窗口 卖出一张票,还剩 23 张
B窗口 卖出一张票,还剩 22 张
A窗口 卖出一张票,还剩 21 张
B窗口 卖出一张票,还剩 20 张
A窗口 卖出一张票,还剩 19 张
B窗口 卖出一张票,还剩 18 张
A窗口 卖出一张票,还剩 17 张
B窗口 卖出一张票,还剩 16 张
A窗口 卖出一张票,还剩 15 张
B窗口 卖出一张票,还剩 14 张
A窗口 卖出一张票,还剩 13 张
B窗口 卖出一张票,还剩 12 张
A窗口 卖出一张票,还剩 11 张
B窗口 卖出一张票,还剩 10 张
A窗口 卖出一张票,还剩 9 张
B窗口 卖出一张票,还剩 8 张
A窗口 卖出一张票,还剩 7 张
B窗口 卖出一张票,还剩 6 张
A窗口 卖出一张票,还剩 5 张
B窗口 卖出一张票,还剩 4 张
A窗口 卖出一张票,还剩 3 张
B窗口 卖出一张票,还剩 2 张
A窗口 卖出一张票,还剩 1 张
B窗口 卖出一张票,还剩 0 张
票已经卖完

Process finished with exit code 0

接下来举一个复杂一些的例子:

import time
import datetime
import threading


def get_time_str():
    now = datetime.datetime.now()
    return datetime.datetime.strftime(now,"%H:%M:%S")


def thread_function(thread_id):
    print("THread %d\t start at %s"%(thread_id,get_time_str()))
    print("Thread %d\t sleeping"%thread_id)
    time.sleep(4)
    print("Thread %d\t finish at %s"%(thread_id,get_time_str()))


def main():
    print("Main thread start at %s"%(get_time_str()))
    threads = []

    #创建线程
    for i in range(5):
        thread = threading.Thread(target=thread_function,args=(i,))
        threads.append(thread)
    #启动线程
    for i in range(5):
        threads[i].start()
        time.sleep(1)
    #等待线程执行完毕
    for i in range(5):
        threads[i].join()#子线程执行完毕之后再执行主线程
    print("Main thread finish at %s"% get_time_str())

if __name__ == "__main__":
    main()

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start at 18:39:12
THread 0	 start at 18:39:12
Thread 0	 sleeping
THread 1	 start at 18:39:13
Thread 1	 sleeping
THread 2	 start at 18:39:14
Thread 2	 sleeping
THread 3	 start at 18:39:15
Thread 3	 sleeping
Thread 0	 finish at 18:39:16
THread 4	 start at 18:39:16
Thread 4	 sleeping
Thread 1	 finish at 18:39:17
Thread 2	 finish at 18:39:18
Thread 3	 finish at 18:39:19
Thread 4	 finish at 18:39:20
Main thread finish at 18:39:20

Process finished with exit code 0

还有一种常见的方法就是我们可以从threading.Thread派生一个子类,在这个子类中调用父类的构造函数并实现run方法即可

扫描二维码关注公众号,回复: 11366719 查看本文章
import time
import datetime
import threading


def get_time_str():
    now = datetime.datetime.now()
    return datetime.datetime.strftime(now,"%H:%M:%S")


class MyThread(threading.Thread):
    #重写父类的构造函数
    def __init__(self,thread_id):
        super(MyThread,self).__init__()
        self.thread_id = thread_id

    def run(self):
        print("Thread %d\t start at %s"%(self.thread_id,get_time_str()))
        print("THread %d\t sleeping"%self.thread_id)
        time.sleep(4)
        print("Thread %d\t finish at %s"%(self.thread_id,get_time_str()))
def main():
    print("Main thread start at %s"%get_time_str())
    threads = []

    #创建线程
    for i in range(5):
        thread = MyThread(i)
        threads.append(thread)

    #启动线程
    for i in range(5):
        threads[i].start()
        time.sleep(1)

    #等待线程执行完毕
    for i in range(5):
        threads[i].join()

    print("Main thread finish at %s"%get_time_str())

if __name__ == "__main__":
    main()

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start at 19:16:21
Thread 0	 start at 19:16:21
THread 0	 sleeping
Thread 1	 start at 19:16:22
THread 1	 sleeping
Thread 2	 start at 19:16:23
THread 2	 sleeping
Thread 3	 start at 19:16:24
THread 3	 sleeping
Thread 0	 finish at 19:16:25
Thread 4	 start at 19:16:25
THread 4	 sleeping
Thread 1	 finish at 19:16:26
Thread 2	 finish at 19:16:27
Thread 3	 finish at 19:16:28
Thread 4	 finish at 19:16:29
Main thread finish at 19:16:29

Process finished with exit code 0

start方法会创立线程,调用一些内部启动方法之后再调用我们实现的run方法

线程同步

多线程的优势在于可以同时运行多个任务,但当线程需要处理同一个资源时,就要考虑数据不同步的问题了,如下所示:

import time
import threading

thread_lock = None


class MyThread(threading.Thread):
    #重写父类的构造函数
    def __init__(self,thread_id):
        super(MyThread,self).__init__()
        self.thread_id = thread_id

    def run(self):
       #锁定
        thread_lock.acquire()
        for i in range(3):
            print("Thread %d\t printing!times:%d" %(self.thread_id,i))
        thread_lock.release()
        time.sleep(1)

        thread_lock.acquire()
        for i in range(3):
            print("Thread %d\t printing!times:%d" % (self.thread_id,i))
        thread_lock.release()

def main():
    print("Main thread start")
    threads = []

    #创建线程
    for i in range(5):
        thread = MyThread(i)
        threads.append(thread)

    #启动线程
    for i in range(5):
        threads[i].start()

    #等待线程执行完毕
    for i in range(5):
        threads[i].join()

    print("Main thread finish at %s")

if __name__ == "__main__":
    thread_lock = threading.Lock();
    main()

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/hahahaha.py
Main thread start
Thread 0	 printing!times:0
Thread 0	 printing!times:1
Thread 0	 printing!times:2
Thread 1	 printing!times:0
Thread 1	 printing!times:1
Thread 1	 printing!times:2
Thread 2	 printing!times:0
Thread 2	 printing!times:1
Thread 2	 printing!times:2
Thread 3	 printing!times:0
Thread 3	 printing!times:1
Thread 3	 printing!times:2
Thread 4	 printing!times:0
Thread 4	 printing!times:1
Thread 4	 printing!times:2
Thread 1	 printing!times:0
Thread 1	 printing!times:1
Thread 1	 printing!times:2
Thread 0	 printing!times:0
Thread 0	 printing!times:1
Thread 0	 printing!times:2
Thread 4	 printing!times:0
Thread 4	 printing!times:1
Thread 4	 printing!times:2
Thread 3	 printing!times:0
Thread 3	 printing!times:1
Thread 3	 printing!times:2
Thread 2	 printing!times:0
Thread 2	 printing!times:1
Thread 2	 printing!times:2
Main thread finish at %s

Process finished with exit code 0

队列

使用共享变量来实现线程之间传递信息和数据需要我们手动控制锁,标椎库提供了一个非常有用的Queue模块,可以帮我们自动控制锁,保证数据同步,Queue类实现了一个基本的先进先出(FIFO)容器,使用put将元素添加到序列尾端,使用get从队列尾端移除元素
Queue模块不一定要使用多线程,例子如下:

from queue import Queue


q = Queue()
for i in range(5):
    q.put(i)
for i in range(5):
    print(q.get())

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/python.py
0
1
2
3
4

Process finished with exit code 0

多线程同步如下:

import time
import threading
import queue


#创建工作队列并且限制队列的最大元素个数是十个
work_queue = queue.Queue(maxsize=10)
#创建结果队列并且限制队列的最大元素个数是十个
result_queue = queue.Queue(maxsize=10)

class WorkerThread(threading.Thread):
    def __init__(self,thread_id):
        super(WorkerThread,self).__init__()
        self.thread_id = thread_id
    def run(self):
        while not work_queue.empty():
            #从工作队列里获取数据
            work = work_queue.get()
            #模拟工作3秒
            time.sleep(3)
            out = "Thread %d\t received %s"%(self.thread_id,work)
            #把结果放入结果队列
            result_queue.put(out)
def main():
    #工作队列放入数据
    for i in range(10):
        work_queue.put("message id %d"%i)
    #开启两个工作线程
    for i in range(2):
        thread = WorkerThread(i)
        thread.start()
    #输出10个结果
    for i in range(10):
        result = result_queue.get()
        print(result)
if __name__ == "__main__":
    main()

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/python.py
Thread 0	 received message id 0
Thread 1	 received message id 1
Thread 1	 received message id 3
Thread 0	 received message id 2
Thread 0	 received message id 5
Thread 1	 received message id 4
Thread 1	 received message id 7
Thread 0	 received message id 6
Thread 0	 received message id 9
Thread 1	 received message id 8

Process finished with exit code 0

进程

from multiprocessing import Process
import time


def run(name):
    print(name,"进程执行了")
    time.sleep(5)

#在Windows系统中创建和执行线程必须要写在if __name__ == '__main__':中
if __name__ == '__main__':
    # 创建线程
    p1 = Process(target=run, args=("p1",))
    p2 = Process(target=run, args=("p2",))
    p3 = Process(target=run, args=("p3",))
    p4 = Process(target=run, args=("p4",))
    p5 = Process(target=run, args=("p5",))

    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p5.start()
#多进程可以真正实现多个任务并行

结果:

E:\anewproject\venv\Scripts\python.exe E:/anewproject/dui.py
p1 进程执行了
p2 进程执行了
p3 进程执行了
p4 进程执行了
p5 进程执行了

猜你喜欢

转载自blog.csdn.net/weixin_43328054/article/details/104156192
今日推荐