您的位置:

使用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 个任务,并由线程池执行。