目录
1.简介
1.1什么事多线程
1.2概念
2.python多线程基础知识讲解
创建线程:
启动线程:
线程函数/方法:
线程同步:
线程间通信:
线程结束与等待:
线程常用方法:
3.c++多线程基础知识讲解
创建线程:
启动线程:
线程函数/方法:
线程同步:
线程间通信:
线程结束与等待:
线程常用方法:
1.简介
1.1什么事多线程
多线程是指在一个程序中同时执行多个线程,每个线程又可以独立地执行不同的任务。在单线程程序中,代码按照顺序依次执行,而多线程程序可以并发执行多个线程,提高了程序的运行效率和响应性。
1.2概念
-
线程与进程:线程是进程的一部分,一个进程可以包含多个线程。进程是一个正在执行中的程序,而线程是在进程内部活动的执行单元。多个线程共享进程的资源,如内存空间和文件句柄。
-
并发与并行:并发是指多个任务交替执行的状态,而并行是指多个任务同时执行的状态。多线程能够实现并发执行,但在单核处理器上只能通过时间片轮转的方式交替执行,而多核处理器可以实现真正的并行执行。
-
线程的创建与启动:在大多数编程语言中,都提供了创建和管理线程的机制。通过创建线程对象并给定需要执行的代码,可以创建一个新的线程。然后,通过启动线程,运行线程中定义的代码。
-
线程同步与互斥:由于多个线程共享资源,可能会导致竞争条件(Race Condition)和数据不一致的问题。为了避免这些问题,需要使用线程同步和互斥机制,如锁(Lock)和信号量(Semaphore),来保证线程间的有序访问共享资源。
-
线程通信:多个线程之间可能需要相互通信和协作。常见的线程通信机制有共享变量、消息队列、信号量等,通过这些机制可以实现线程间的数据传递和同步操作。
-
线程调度:线程调度决定了线程在多个可执行线程中的执行顺序和时间片分配。具体的调度策略与操作系统相关,可以是预先定义的优先级调度、时间片轮转、抢占式调度等。
-
线程安全性:线程安全性是指多线程环境中,对共享资源的访问和操作不会引发并发问题。编写线程安全的代码需要考虑数据的原子性、可见性和有序性,避免并发访问导致的错误。
2.python多线程基础知识讲解
Python 提供了 threading
模块,它是一个内置的模块,用于创建和管理线程。threading
模块提供了一些类和函数,使得创建和控制线程变得简单。
-
创建线程:
- 使用
threading.Thread
类可以创建一个新的线程对象。在实例化线程对象时,需要指定线程要执行的函数或方法,并可以传递参数给该函数或方法。 - 创建线程的基本语法为:
thread_obj = threading.Thread(target=func, args=args)
,其中target
指定函数或方法名,args
是一个元组,包含传递给函数或方法的参数。
- 使用
-
启动线程:
- 使用
start()
方法启动线程。调用线程对象的start()
方法后,线程会开始执行指定的函数或方法。 - 注意,不要直接调用线程函数或方法,而是使用
start()
方法启动线程,否则线程不会以并发的方式执行,而会以串行的方式执行。
- 使用
-
线程函数/方法:
- 线程要执行的代码写在线程函数(对于普通函数)或线程方法(对于类的方法)中。线程会按顺序执行这些代码。
- 在线程函数或方法中可以使用
time.sleep()
函数来模拟耗时操作,以便让线程有时间片轮转的机会。
-
线程同步:
- 多个线程可能会同时访问和修改共享的数据,因此需要进行线程同步以避免竞争条件和数据不一致。
- 在 Python 中,可以使用锁(Lock)来实现线程同步。通过锁定共享资源,只允许一个线程访问,其他线程需要等待解锁后才能访问。
threading
模块提供了Lock
类,可以在需要同步的代码块中使用acquire()
和release()
方法来获取和释放锁。
-
线程间通信:
- 多个线程之间可能需要进行通信和协作。如果线程之间需要传递数据,可以使用共享变量、队列、事件等线程安全的数据结构。
- Python 提供了
Queue
类用于线程安全的队列操作,可以在不同的线程间安全地传递数据。
-
线程结束与等待:
- 使用
join()
方法可以等待线程完成。调用线程对象的join()
方法会阻塞调用线程,直到目标线程执行完毕。 - 可以通过设置
timeout
参数来指定等待的超时时间,如果超过超时时间线程仍未结束,则继续执行。
- 使用
-
线程常用方法:
start()
: 启动线程。join(timeout)
: 等待线程结束。is_alive()
: 检查线程是否在运行中。getName()
,setName()
: 获取和设置线程名称。
这些是 Python 多线程编程的基本概念和常用操作。在实际开发中,需要注意线程安全性、资源的共享与同步、优化线程的数量等问题,以充分发挥多线程的优势。
下面给大家简单举个例子讲解一下
import threading
import time
# 线程函数
def thread_func(name, delay):
print("线程 {} 开始".format(name))
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("线程 {} 执行,计数器:{}".format(name, count))
print("线程 {} 结束".format(name))
# 创建并启动两个线程
if __name__ == "__main__":
thread1 = threading.Thread(target=thread_func, args=("Thread 1", 1))
thread2 = threading.Thread(target=thread_func, args=("Thread 2", 2))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("主线程结束")
在这个例子中,我们通过 threading.Thread
类创建了两个线程 thread1
和 thread2
,每个线程都执行相同的 thread_func
函数。thread_func
函数接受两个参数,分别是线程的名称和延迟时间。
在线程函数内部,使用 time.sleep
函数模拟一段耗时操作,实际中可以是任何需要并发执行的任务。每个线程会依次执行这个耗时操作,并在控制台输出执行的信息。
在主线程中,我们先调用 thread1.start()
和 thread2.start()
启动两个线程,然后调用 thread1.join()
和 thread2.join()
等待两个线程执行结束。最后,主线程输出 "主线程结束"。
执行这段代码,你会看到两个线程按照不同的延迟时间交替执行,而主线程会等待两个子线程执行完毕后才会结束。
这个例子展示了如何使用 Python 的 threading
模块创建和管理多线程,实现简单的并发执行。请注意,在真正的应用中,需要注意线程同步、资源访问的安全性等问题,以避免并发问题。
3.c++多线程基础知识讲解
C++ 标准库提供了 <thread>
头文件,其中定义了一些类和函数,用于创建和管理线程。C++11 引入了对多线程的支持,使得使用多线程变得更加方便。
-
创建线程:
- 使用
std::thread
类可以创建一个新的线程对象。在实例化线程对象时,需要指定线程要执行的函数或方法,并可以传递参数给该函数或方法。 - 创建线程的基本语法为:
std::thread thread_obj(func, args)
,其中func
指定函数或方法名,args
是一个参数列表,包含传递给函数或方法的参数。
- 使用
-
启动线程:
- 使用
thread_obj.join()
方法启动线程。调用线程对象的join()
方法后,线程会开始执行指定的函数或方法。 - 注意,不要直接调用线程函数或方法,而是使用
join()
方法启动线程,否则线程不会以并发的方式执行,而会以串行的方式执行。
- 使用
-
线程函数/方法:
- 线程要执行的代码写在线程函数(对于普通函数)或线程方法(对于类的成员函数)中。线程会按顺序执行这些代码。
- 在线程函数或方法中可以使用
std::this_thread::sleep_for()
函数来模拟耗时操作,以便让线程有时间片轮转的机会。
-
线程同步:
- 多个线程可能会同时访问和修改共享的数据,因此需要进行线程同步以避免竞争条件和数据不一致。
- 在 C++ 中,可以使用互斥锁(
std::mutex
)来实现线程同步。通过锁定共享资源,只允许一个线程访问,其他线程需要等待解锁后才能访问。 - 互斥锁提供了
lock()
和unlock()
方法来获取和释放锁。
-
线程间通信:
- 多个线程之间可能需要进行通信和协作。如果线程之间需要传递数据,可以使用共享变量、条件变量等线程安全的数据结构。
- C++ 中的条件变量(
std::condition_variable
)用于在线程之间进行条件等待和唤醒操作,实现线程间的同步与通信。
-
线程结束与等待:
- 使用
thread_obj.join()
方法可以等待线程完成。调用线程对象的join()
方法会阻塞调用线程,直到目标线程执行完毕。 - 可以通过设置
timeout
参数来指定等待的超时时间,如果超过超时时间线程仍未结束,则继续执行。
- 使用
-
线程常用方法:
join()
: 等待线程结束。joinable()
: 检查线程是否可以被join()
。detach()
: 分离线程,使其在执行完毕后自动释放资源。get_id()
: 获取线程的唯一标识符。
这些是 C++ 中多线程操作的基本概念和常用操作。在实际开发中,需要注意线程安全性、资源的共享与同步、优化线程的数量等问题,以充分发挥多线程的优势。
下面是一个简单的 C++ 多线程的例子,我们将使用多个线程计算数组中元素的和,并输出结果。
#include <iostream>
#include <thread>
#include <vector>
// 线程函数:计算数组片段的和
void calculateSum(const std::vector<int>& arr, int start, int end, int& sum)
{
sum = 0;
for (int i = start; i < end; i++) {
sum += arr[i];
}
}
int main()
{
std::vector<int> arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int numThreads = 4; // 线程数量
int arrSize = arr.size();
std::vector<std::thread> threads(numThreads); // 创建线程对象的容器
std::vector<int> partialSums(numThreads); // 存储每个线程计算得到的和
// 启动线程,并计算每个线程要计算的数组片段
int step = arrSize / numThreads; // 计算每个线程处理的元素数量
for (int i = 0; i < numThreads; i++) {
int start = i * step;
int end = (i == numThreads - 1) ? arrSize : (start + step);
threads[i] = std::thread(calculateSum, std::ref(arr), start, end, std::ref(partialSums[i]));
}
// 等待线程结束
for (auto& thread : threads) {
thread.join();
}
// 计算所有线程的和
int totalSum = 0;
for (int i = 0; i < numThreads; i++) {
totalSum += partialSums[i];
}
std::cout << "数组元素的总和为:" << totalSum << std::endl;
return 0;
}
代码解释:
- 在
calculateSum
函数中,每个线程计算数组的一个片段。该函数接受一个引用的数组、起始索引、结束索引和一个引用的变量sum
,并将计算的结果保存在sum
中。 - 在
main
函数中,我们首先定义了一个包含数组元素的向量arr
,然后指定了要使用的线程数量numThreads
,以及数组的大小arrSize
。 - 创建了两个向量
threads
和partialSums
,分别用于存储线程对象和每个线程计算得到的和。 - 根据线程数量
numThreads
和数组大小arrSize
,计算了每个线程要处理的元素数量step
。 - 使用
for
循环创建了numThreads
个线程,并分别传递给它们不同的数组片段进行计算。注意,我们使用std::ref
来传递引用类型的参数。 - 使用另一个
for
循环等待所有线程执行完毕,通过调用join()
方法来实现线程同步。 - 最后,计算所有线程计算得到的和,保存在
totalSum
中,并输出结果。
这是一个简单的多线程例子,通过将数组分成多个片段,使用多个线程并行计算,并最后合并结果。请注意,在实际开发中,需要考虑线程之间的同步和共享数据的安全性,以避免竞争条件等问题。
以此记录自己的多线程学习之旅!
评论区欢迎留言,大家一起学习