多线程与并行计算(python与c++)基础入门篇

目录

1.简介

1.1什么事多线程

1.2概念

2.python多线程基础知识讲解

创建线程:

启动线程:

线程函数/方法:

线程同步:

线程间通信:

线程结束与等待:

线程常用方法:

3.c++多线程基础知识讲解

创建线程:

启动线程:

线程函数/方法:

线程同步:

线程间通信:

线程结束与等待:

线程常用方法:


1.简介

1.1什么事多线程

多线程是指在一个程序中同时执行多个线程,每个线程又可以独立地执行不同的任务。在单线程程序中,代码按照顺序依次执行,而多线程程序可以并发执行多个线程,提高了程序的运行效率和响应性。

1.2概念

  1. 线程与进程:线程是进程的一部分,一个进程可以包含多个线程。进程是一个正在执行中的程序,而线程是在进程内部活动的执行单元。多个线程共享进程的资源,如内存空间和文件句柄。

  2. 并发与并行:并发是指多个任务交替执行的状态,而并行是指多个任务同时执行的状态。多线程能够实现并发执行,但在单核处理器上只能通过时间片轮转的方式交替执行,而多核处理器可以实现真正的并行执行。

  3. 线程的创建与启动:在大多数编程语言中,都提供了创建和管理线程的机制。通过创建线程对象并给定需要执行的代码,可以创建一个新的线程。然后,通过启动线程,运行线程中定义的代码。

  4. 线程同步与互斥:由于多个线程共享资源,可能会导致竞争条件(Race Condition)和数据不一致的问题。为了避免这些问题,需要使用线程同步和互斥机制,如锁(Lock)和信号量(Semaphore),来保证线程间的有序访问共享资源。

  5. 线程通信:多个线程之间可能需要相互通信和协作。常见的线程通信机制有共享变量、消息队列、信号量等,通过这些机制可以实现线程间的数据传递和同步操作。

  6. 线程调度:线程调度决定了线程在多个可执行线程中的执行顺序和时间片分配。具体的调度策略与操作系统相关,可以是预先定义的优先级调度、时间片轮转、抢占式调度等。

  7. 线程安全性:线程安全性是指多线程环境中,对共享资源的访问和操作不会引发并发问题。编写线程安全的代码需要考虑数据的原子性、可见性和有序性,避免并发访问导致的错误。

2.python多线程基础知识讲解

Python 提供了 threading 模块,它是一个内置的模块,用于创建和管理线程。threading 模块提供了一些类和函数,使得创建和控制线程变得简单。

  1. 创建线程:

    • 使用 threading.Thread 类可以创建一个新的线程对象。在实例化线程对象时,需要指定线程要执行的函数或方法,并可以传递参数给该函数或方法。
    • 创建线程的基本语法为:thread_obj = threading.Thread(target=func, args=args),其中 target 指定函数或方法名,args 是一个元组,包含传递给函数或方法的参数。
  2. 启动线程:

    • 使用 start() 方法启动线程。调用线程对象的 start() 方法后,线程会开始执行指定的函数或方法。
    • 注意,不要直接调用线程函数或方法,而是使用 start() 方法启动线程,否则线程不会以并发的方式执行,而会以串行的方式执行。
  3. 线程函数/方法:

    • 线程要执行的代码写在线程函数(对于普通函数)或线程方法(对于类的方法)中。线程会按顺序执行这些代码。
    • 在线程函数或方法中可以使用 time.sleep() 函数来模拟耗时操作,以便让线程有时间片轮转的机会。
  4. 线程同步:

    • 多个线程可能会同时访问和修改共享的数据,因此需要进行线程同步以避免竞争条件和数据不一致。
    • 在 Python 中,可以使用锁(Lock)来实现线程同步。通过锁定共享资源,只允许一个线程访问,其他线程需要等待解锁后才能访问。
    • threading 模块提供了 Lock 类,可以在需要同步的代码块中使用 acquire()release() 方法来获取和释放锁。
  5. 线程间通信:

    • 多个线程之间可能需要进行通信和协作。如果线程之间需要传递数据,可以使用共享变量、队列、事件等线程安全的数据结构。
    • Python 提供了 Queue 类用于线程安全的队列操作,可以在不同的线程间安全地传递数据。
  6. 线程结束与等待:

    • 使用 join() 方法可以等待线程完成。调用线程对象的 join() 方法会阻塞调用线程,直到目标线程执行完毕。
    • 可以通过设置 timeout 参数来指定等待的超时时间,如果超过超时时间线程仍未结束,则继续执行。
  7. 线程常用方法:

    • 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 类创建了两个线程 thread1thread2,每个线程都执行相同的 thread_func 函数。thread_func 函数接受两个参数,分别是线程的名称和延迟时间。

在线程函数内部,使用 time.sleep 函数模拟一段耗时操作,实际中可以是任何需要并发执行的任务。每个线程会依次执行这个耗时操作,并在控制台输出执行的信息。

在主线程中,我们先调用 thread1.start()thread2.start() 启动两个线程,然后调用 thread1.join()thread2.join() 等待两个线程执行结束。最后,主线程输出 "主线程结束"。

执行这段代码,你会看到两个线程按照不同的延迟时间交替执行,而主线程会等待两个子线程执行完毕后才会结束。

这个例子展示了如何使用 Python 的 threading 模块创建和管理多线程,实现简单的并发执行。请注意,在真正的应用中,需要注意线程同步、资源访问的安全性等问题,以避免并发问题。

3.c++多线程基础知识讲解

C++ 标准库提供了 <thread> 头文件,其中定义了一些类和函数,用于创建和管理线程。C++11 引入了对多线程的支持,使得使用多线程变得更加方便。

  1. 创建线程:

    • 使用 std::thread 类可以创建一个新的线程对象。在实例化线程对象时,需要指定线程要执行的函数或方法,并可以传递参数给该函数或方法。
    • 创建线程的基本语法为:std::thread thread_obj(func, args),其中 func 指定函数或方法名,args 是一个参数列表,包含传递给函数或方法的参数。
  2. 启动线程:

    • 使用 thread_obj.join() 方法启动线程。调用线程对象的 join() 方法后,线程会开始执行指定的函数或方法。
    • 注意,不要直接调用线程函数或方法,而是使用 join() 方法启动线程,否则线程不会以并发的方式执行,而会以串行的方式执行。
  3. 线程函数/方法:

    • 线程要执行的代码写在线程函数(对于普通函数)或线程方法(对于类的成员函数)中。线程会按顺序执行这些代码。
    • 在线程函数或方法中可以使用 std::this_thread::sleep_for() 函数来模拟耗时操作,以便让线程有时间片轮转的机会。
  4. 线程同步:

    • 多个线程可能会同时访问和修改共享的数据,因此需要进行线程同步以避免竞争条件和数据不一致。
    • 在 C++ 中,可以使用互斥锁(std::mutex)来实现线程同步。通过锁定共享资源,只允许一个线程访问,其他线程需要等待解锁后才能访问。
    • 互斥锁提供了 lock()unlock() 方法来获取和释放锁。
  5. 线程间通信:

    • 多个线程之间可能需要进行通信和协作。如果线程之间需要传递数据,可以使用共享变量、条件变量等线程安全的数据结构。
    • C++ 中的条件变量(std::condition_variable)用于在线程之间进行条件等待和唤醒操作,实现线程间的同步与通信。
  6. 线程结束与等待:

    • 使用 thread_obj.join() 方法可以等待线程完成。调用线程对象的 join() 方法会阻塞调用线程,直到目标线程执行完毕。
    • 可以通过设置 timeout 参数来指定等待的超时时间,如果超过超时时间线程仍未结束,则继续执行。
  7. 线程常用方法:

    • 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
  • 创建了两个向量 threadspartialSums,分别用于存储线程对象和每个线程计算得到的和。
  • 根据线程数量 numThreads 和数组大小 arrSize,计算了每个线程要处理的元素数量 step
  • 使用 for 循环创建了 numThreads 个线程,并分别传递给它们不同的数组片段进行计算。注意,我们使用 std::ref 来传递引用类型的参数。
  • 使用另一个 for 循环等待所有线程执行完毕,通过调用 join() 方法来实现线程同步。
  • 最后,计算所有线程计算得到的和,保存在 totalSum 中,并输出结果。

这是一个简单的多线程例子,通过将数组分成多个片段,使用多个线程并行计算,并最后合并结果。请注意,在实际开发中,需要考虑线程之间的同步和共享数据的安全性,以避免竞争条件等问题。

以此记录自己的多线程学习之旅!

评论区欢迎留言,大家一起学习

猜你喜欢

转载自blog.csdn.net/weixin_45303602/article/details/132409582