Bootstrap

跟我一起从零开始学python(三)多线程/多进程/协程

前言

回顾之前讲了python语法编程 ,关于从零入门python的第一遍,编程语法必修内容,比如python3基础入门,列表与元组,字符串,字典,条件丶循环和其他语句丶函数丶面向对象丶异常和文件处理和网络编程篇

1.跟我一起从零开始学python(一)编程语法必修
2.跟我一起从零开始学python(二)网络编程

本篇讲:python并发编程:多线程/多进程/协程

本系列文根据以下学习路线展开讲述,由于内容较多:

从零开始学python到高级进阶路线图

关注公众号:python技术训练营,学习进阶一步到位

适用于零基础学习和进阶人群的python资源:

① 腾讯认证python完整项目实战教程笔记PDF
② 十几个大厂python面试专题PDF
③ python全套视频教程(零基础-高级进阶JS逆向)
④ 百个项目实战+源码+笔记
⑤ 编程语法-机器学习-全栈开发-数据分析-爬虫-APP逆向等全套项目+文档
⑥ 交流学习
⑦ 想要兼职接单

第一章:多线程

1.线程和进程

线程和进程是操作系统中的两个重要概念,它们都是并发编程的基础。线程是操作系统能够进行运算调度的最小单位,而进程则是操作系统进行资源分配和调度的基本单位。

线程和进程的区别:

  • 线程是进程的一部分,一个进程可以包含多个线程,而一个线程只能属于一个进程。
  • 进程拥有独立的内存空间,而线程共享进程的内存空间。
  • 进程之间的通信需要使用IPC(Inter-Process Communication)机制,而线程之间可以直接共享数据。
  • 进程的创建和销毁比线程慢,因为进程需要分配和释放独立的内存空间,而线程只需要分配和释放一些寄存器和栈空间。

在Python中,可以使用threading模块来创建和管理线程。下面是一个简单的线程示例:

import threading

def worker():
    print('Worker thread started')
    # do some work here
    print('Worker thread finished')

# create a new thread
t = threading.Thread(target=worker)
# start the thread
t.start()
# wait for the thread to finish
t.join()

在这个示例中,我们创建了一个名为worker的函数,它将在一个新的线程中运行。我们使用threading.Thread类创建了一个新的线程对象,并将worker函数作为目标传递给它。然后,我们使用start()方法启动线程,并使用join()方法等待线程完成。

2.使用线程

在Python中,使用线程可以通过threading模块来实现。下面是一个简单的例子,展示了如何使用线程:

import threading

def worker():
    """线程执行的任务"""
    print("Worker thread started")
    # 执行一些任务
    print("Worker thread finished")

# 创建线程
t = threading.Thread(target=worker)
# 启动线程
t.start()

# 主线程继续执行其他任务
print("Main thread finished")

在上面的例子中,我们首先定义了一个worker函数,它将在一个单独的线程中执行。然后,我们使用threading.Thread类创建了一个新的线程,并将worker函数作为参数传递给它。最后,我们调用start方法来启动线程。

注意,线程是异步执行的,因此主线程不会等待线程完成。在上面的例子中,主线程会立即继续执行,输出Main thread finished。如果我们希望等待线程完成后再继续执行主线程,可以使用join方法:

# 等待线程完成
t.join()


# 主线程继续执行其他任务
print("Main thread finished")

在上面的代码中,我们在启动线程后调用了t.join()方法,这将阻塞主线程,直到线程完成。然后,主线程才会继续执行。

3.多线程全局变量

在多线程编程中,多个线程可以共享全局变量。但是需要注意的是,多个线程同时对同一个全局变量进行读写操作时,可能会出现数据竞争(Data Race)的问题,导致程序出现不可预期的结果。

为了避免数据竞争,可以使用线程锁(Thread Lock)来保证同一时刻只有一个线程可以访问共享变量。Python中提供了LockRLockSemaphore等多种锁机制,可以根据实际需求选择合适的锁。

下面是一个使用Lock来保证多线程共享全局变量安全的示例代码:

import threading

# 定义全局变量
count = 0

# 定义线程锁
lock = threading.Lock()

# 定义线程函数
def add():
    global count
    for i in range(100000):
        # 获取锁
        lock.acquire()
        count += 1
        # 释放锁
        lock.release()

# 创建两个线程
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)

# 启动线程
t1.start()
t2.start()

# 等待线程执行完毕
t1.join()
t2.join()

# 输出结果
print(count)

在上面的代码中,我们定义了一个全局变量count,并使用Lock来保证多个线程对count进行读写操作时的安全性。在每个线程中,我们首先获取锁,然后对count进行加1操作,最后释放锁。这样就可以保证同一时刻只有一个线程可以访问count,避免了数据竞争的问题。

需要注意的是,使用锁会带来一定的性能损失,因为每次获取锁和释放锁都需要一定的时间。因此,在实际应用中,需要根据实际情况来选择合适的锁机制,避免过度使用锁导致程序性能下降。

4.共享全局变量所带来的问题

在多线程编程中,多个线程可以共享全局变量。但是,共享全局变量也会带来一些问题:

  • 竞争条件:当多个线程同时访问和修改同一个全局变量时ÿ

;