c  多线程系列二 自定义线程执行器

一、背景

在多线程编程中,线程池是一种常见的机制,它可以减少线程创建和销毁的开销,并控制同时执行的任务数量,防止过多的线程竞争资源导致性能下降。C++11 中的标准库提供了一个 std::thread,但是并没有提供线程池的实现,只能通过手动创建线程池来实现。在实际的项目中,线程池的需求是非常常见的,为了方便使用和管理线程池,我们可以自定义一个线程池执行器。

二、基本概念

1. 执行器

执行器是一种负责管理线程的对象。它有一个队列用来保存待执行的任务,当有空闲的线程时,它会从队列中获取一个任务并由线程执行。

2. 任务

任务是一个可执行的代码块,执行器会将任务保存到队列中,线程可以从队列中获取任务并执行。

3. 线程

线程是计算机程序中的一个执行流程。线程可以并行执行,多个线程可以共享进程中的数据和资源,使用线程可以提高程序的处理效率。

4. 线程池

线程池是在进程启动时创建一定数量的线程,这些线程在执行任务时不是一直存在,而是将执行完一个任务后,再去接管下一个任务。这样做的好处是避免了创建线程以及线程上下文切换的开销,提高了程序的效率,而且还可以避免瞬间有大量的任务到达而导致程序崩溃。

三、自定义线程执行器

在上述基本概念的基础上,我们可以自定义一个线程执行器:

```cpp

class ThreadPool

{

public:

ThreadPool(int maxThreads = 4);

~ThreadPool();

template

auto submit(F&& f, Args&&... args) -> std::future::type>;

private:

std::vector m_threads;

std::queue> m_tasks;

std::mutex m_taskMutex;

std::condition_variable m_taskCondition;

bool m_stop;

};

// 构造函数

ThreadPool::ThreadPool(int maxThreads)

:m_stop(false)

{

maxThreads = std::max(1, maxThreads);

for (int i = 0; i < maxThreads; ++i)

{

m_threads.emplace_back(

[this]()

{

while (true)

{

std::function task;

{

std::unique_lock lock(m_taskMutex);

m_taskCondition.wait(lock, [this]() {return m_stop || !m_tasks.empty(); });

if (m_stop && m_tasks.empty())

return;

task = std::move(m_tasks.front());

m_tasks.pop();

}

task();

}

}

);

}

}

// 析构函数

ThreadPool::~ThreadPool()

{

{

std::unique_lock lock(m_taskMutex);

m_stop = true;

}

m_taskCondition.notify_all();

for (auto& thread : m_threads)

{

thread.join();

}

}

// 提交任务

template

auto ThreadPool::submit(F&& f, Args&&... args) -> std::future::type>

{

using return_type = typename std::result_of::type;

auto task = std::make_shared>(std::bind(std::forward(f), std::forward(args)...));

std::future result = task->get_future();

{

std::unique_lock lock(m_taskMutex);

if (m_stop)

{

throw std::runtime_error("submit on stopped ThreadPool");

}

m_tasks.emplace([task]() {(*task)(); });

}

m_taskCondition.notify_one();

return result;

}

```

四、使用方法

使用线程执行器很简单,只需要使用 submit() 函数提交一个任务即可。代码如下:

```cpp

ThreadPool pool(4); // 创建线程池,最大线程数量为4个

std::vector> results;

// 向线程池提交任务

for (int i = 0; i < 8; ++i)

{

results.emplace_back(

pool.submit([i]() -> int

{

std::cout << "hello " << i << std::endl;

std::this_thread::sleep_for(std::chrono::seconds(1));

std::cout << "world " << i << std::endl;

return i * i;

})

);

}

// 获取任务执行结果

for (auto&& result : results)

{

std::cout << result.get() << " ";

}

std::cout << std::endl;

```

五、案例说明

下面我们通过一个具体的案例来说明线程执行器的使用。假设我们要对一组整数排序,但是排序的算法比较耗时,我们可以将排序任务提交到线程池中运行,然后在主线程中等待结果。

```cpp

#include

#include

#include

#include

#include

#include

#include "ThreadPool.h" // 含有线程池的头文件

void sortArray(std::vector& arr)

{

std::sort(arr.begin(), arr.end());

}

int main()

{

// 创建线程池,最大线程数量为4个

ThreadPool pool(4);

std::vector> results;

// 创建一个随机整数数组

std::vector arr;

for (int i = 0; i < 10000; ++i)

{

arr.push_back(std::rand() % 1000);

}

// 将排序任务提交到线程池中

for (int i = 0; i < 4; ++i)

{

auto future = pool.submit(sortArray, std::ref(arr));

results.emplace_back(std::move(future));

}

// 等待任务完成

for (auto&& result : results)

{

result.get();

}

// 输出排序后的数组

for (int i = 0; i < 10; ++i)

{

std::cout << arr[i] << " ";

}

std::cout << std::endl;

return 0;

}

```

运行结果如下:

```

0 0 0 0 0 0 0 0 0 0

```

注意,由于每个线程都在原地排序数组,而没有进行合并操作,因此输出结果会变成全 0 数组。这只是为了演示提交任务的过程,并不是一个正确的多线程排序实现。

六、总结

本文介绍了自定义线程执行器,它是基于 std::thread、std::mutex 和 std::condition_variable 等线程库组件实现的。使用线程执行器可以方便地管理和调度多线程任务,可以通过它提高程序的性能和效率。同时,线程执行器的实现思路也可以用于开发其他的并发框架和工具。 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.ynyuzhu.com/

点赞(23) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部