使用C++在Linux中创建线程

一、使用 pthread_create 函数创建线程

pthread_create 是Linux下常见的创建线程的方法,它包含在 pthread 库中。通过它我们可以在一个程序中创建多个线程来并行处理任务。

下面是一个简单的示例,通过 pthread_create 创建两个线程:

#include <iostream>
#include <pthread.h>

using namespace std;

void* threadFunc(void* arg)
{
    int num = *((int*) arg);
    cout << "Thread #" << num << ": hello world!" << endl;
    return NULL;
}

int main(int argc, char** argv)
{
    pthread_t thread1, thread2;
    int thread1Num = 1, thread2Num = 2;

    pthread_create(&thread1, NULL, threadFunc, &thread1Num);
    pthread_create(&thread2, NULL, threadFunc, &thread2Num);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    cout << "Main thread exiting" << endl;
    return 0;
}

在上面的代码中,我们通过调用 pthread_create 函数来创建两个线程 thread1 和 thread2。每个线程都调用 threadFunc 函数,在这个函数里我们输出了一个简单的 “hello world”。最后,我们通过调用 pthread_join 函数来等待两个线程的结束,然后程序才能正常退出。

二、使用 std::thread 类创建线程

C++11 标准引入了 std::thread 类,可以方便地创建线程。相比于 pthread_create 函数,std::thread 更具有面向对象的特征,更加易于使用。

下面是一个使用 std::thread 创建线程的示例:

#include <iostream>
#include <thread>

using namespace std;

void threadFunc(int num)
{
    cout << "Thread #" << num << ": hello world!" << endl;
}

int main(int argc, char** argv)
{
    thread thread1(threadFunc, 1);
    thread thread2(threadFunc, 2);

    thread1.join();
    thread2.join();

    cout << "Main thread exiting" << endl;
    return 0;
}

在上面的代码中,我们通过使用 std::thread 类创建了两个线程 thread1 和 thread2。和 pthread_create 不同的是,我们使用了类的构造函数来创建线程,也就是说,每个线程都是一个对象。在构造线程的时候,我们将要执行的函数和它的参数作为参数传入线程构造函数。和 pthread_create 一样,我们也需要调用 join() 函数等待线程结束。

三、线程同步与互斥

在多线程程序中,通常都需要处理线程同步和互斥的问题。如果多个线程同时访问同一个数据,可能会导致数据出错或者延时。因此,我们需要使用锁来控制对共享资源的访问,避免多个线程同时访问同一个数据。

下面是一个使用 pthreads 库来保护临界段的示例:

#include <iostream>
#include <pthread.h>

using namespace std;

int count = 0;
pthread_mutex_t count_mutex;

void* threadFunc(void* arg)
{
    for (int i = 0; i < 5; i++) {
        pthread_mutex_lock(&count_mutex);
        count++;
        cout << "Thread #" << *((int*) arg) << " count = " << count << endl;
        pthread_mutex_unlock(&count_mutex);
    }
    return NULL;
}

int main(int argc, char** argv)
{
    pthread_t thread1, thread2;
    int thread1Num = 1, thread2Num = 2;

    pthread_mutex_init(&count_mutex, NULL);

    pthread_create(&thread1, NULL, threadFunc, &thread1Num);
    pthread_create(&thread2, NULL, threadFunc, &thread2Num);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    pthread_mutex_destroy(&count_mutex);

    cout << "Main thread exiting" << endl;
    return 0;
}

在上面的示例中,我们使用了 pthreads 库的互斥量来保护全局变量 count。在 threadFunc 函数中,我们使用 pthread_mutex_lock 和 pthread_mutex_unlock 函数来加锁和解锁。这样,当两个线程同时访问 count 变量时,一次只能有一个线程进行访问,避免了数据错乱。

除了使用互斥量外,C++11 中也提供了 std::mutex 来进行锁操作。std::mutex 的用法和 pthread_mutex_t 是类似的,使用起来更加方便。

四、线程池

线程池是一种常见的多线程编程技术,通常用于处理大量并发请求。线程池中创建好多个线程,等待队列中的任务。当任务来临时,线程池中的线程会从队列中取出任务进行处理,完成后再次回到队列中等待新的任务。

下面是一个简单的线程池实现:

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

using namespace std;

class ThreadPool {
public:
    ThreadPool(int numThreads) {
        for (int i = 0; i < numThreads; i++) {
            threads.emplace_back([this] {
                for (;;) {
                    function<void()> task;
                    {
                        unique_lock<mutex> lock(this->queueMutex);
                        this->condition.wait(lock, [this] {
                            return !this->tasks.empty() || this->stop;
                        });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool() {
        {
            unique_lock<mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        for (auto &thread : threads) {
            thread.join();
        }
    }

    template <class Func, class... Args>
    auto enqueue(Func&& func, Args&&... args) -> future<typename result_of<Func(Args...)>::type> {
        using return_type = typename result_of<Func(Args...)>::type;
        auto task = make_shared<packaged_task<return_type()>>(
            bind(forward<Func>(func), forward<Args>(args)...)
        );
        future<return_type> res = task->get_future();
        {
            unique_lock<mutex> lock(queueMutex);
            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

private:
    vector<thread> threads;
    queue<function<void()>> tasks;
    mutex queueMutex;
    condition_variable condition;
    bool stop = false;
};

void threadFunc(int num)
{
    cout << "Thread #" << num << " hello world!" << endl;
}

int main(int argc, char** argv)
{
    ThreadPool pool(4);

    pool.enqueue(threadFunc, 1);
    pool.enqueue(threadFunc, 2);
    pool.enqueue(threadFunc, 3);
    pool.enqueue(threadFunc, 4);

    return 0;
}

在上面的示例中,我们实现了一个简单的线程池。它包含了一个线程池的类 ThreadPool。它使用 std::vector 存储所有线程,std::queue 存储所有任务,std::mutex和 std::condition_variable 用于线程同步。

线程池的构造函数需要输入线程数量 numThreads 作为参数,它会在构造函数中创建 numThreads 个线程。enqueue 函数可以添加新任务到队列中,添加的任务将会在队列中等待线程池中的线程去执行。

在 main 函数中,我们创建了一个线程池 pool,并且通过 pool.enqueue 函数添加了 4 个任务,并由线程池执行。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/240097.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-12 12:19
下一篇 2024-12-12 12:19

相关推荐

  • Python线程等待指南

    本文将从多个方面详细讲解Python线程等待的相关知识。 一、等待线程结束 在多线程编程中,经常需要等待线程执行完毕再进行下一步操作。可以使用join()方法实现等待线程执行完毕再…

    编程 2025-04-29
  • Python两个线程交替打印1到100

    这篇文章的主题是关于Python多线程的应用。我们将会通过实际的代码,学习如何使用Python两个线程交替打印1到100。 一、创建线程 在Python中,我们可以使用Thread…

    编程 2025-04-28
  • ROS线程发布消息异常解决方法

    针对ROS线程发布消息异常问题,我们可以从以下几个方面进行分析和解决。 一、检查ROS代码是否正确 首先,我们需要检查ROS代码是否正确。可能会出现的问题包括: 是否正确初始化RO…

    编程 2025-04-28
  • Python线程池并发爬虫

    Python线程池并发爬虫是实现多线程爬取数据的常用技术之一,可以在一定程度上提高爬取效率和数据处理能力。本文将从多个方面对Python线程池并发爬虫做详细的阐述,包括线程池的实现…

    编程 2025-04-27
  • 如何在Linux中添加用户并修改配置文件

    本文将从多个方面详细介绍在Linux系统下如何添加新用户并修改配置文件 一、添加新用户 在Linux系统下创建新用户非常简单,只需使用adduser命令即可。使用以下命令添加新用户…

    编程 2025-04-27
  • 如何解决linux jar包 invalid or corrupt jarfile问题

    对于许多开发人员和系统管理员在Linux环境下使用Java开发过程中遇到的一个常见的问题是 invalid or corrupt jarfile(无效或损坏的jar文件)错误。当您…

    编程 2025-04-27
  • 线程池中的一个线程异常了会被怎么处理

    本文将从以下几个方面对线程池中的一个线程异常了会被怎么处理进行详细阐述:异常的类型、如何捕获异常、异常的处理方式。 一、异常的类型 在线程池中,可以出现多种类型的异常,例如线程执行…

    编程 2025-04-27
  • 在Linux上安装JRE并配置环境变量

    本文将从以下几个方面为您详细阐述如何在Linux系统上,通过自己账户安装JRE,并且配置环境变量。 一、安装JRE 在进行安装前,我们需要下载JRE的安装包并解压,可以从官方网站下…

    编程 2025-04-27
  • GTKAM:Linux下的照片管理器

    GTKAM是用于Linux操作系统的一款照片管理器,它支持多种相机及存储设备,并提供了一系列强大的工具,让用户可以方便地浏览、管理、编辑和导出照片。本文将从多个方面对GTKAM进行…

    编程 2025-04-27
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25

发表回复

登录后才能评论