线程

- 一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 因为虚拟内存的关系不同的进程互相不知道对方的存在,相对独立,互相有独立的内存空间,地址空间;
- 相同进程内的线程之间共享进程的内存空间,文件描述符表,部分寄存器等资源,但是每个线程都有自己独立的线程栈和部分寄存器,线程间访问资源需要考虑互斥问题。
- 进程创建,销毁,切换代价大;
- 线程创建,销毁,切换小。
- 进程有利于资源管理和保护,开销大;
- 线程不利于资源管理和保护,需要使用锁保证线程的安全运行,开销小
- 不同进程间的线程通信等同于进程通信,一般是五种通信方式:
消息队列;
共享内存;
有名管道/无名管道;
信号;
scoket。 - 相同进程中的不同线程因为资源共享不存在通信问题,主要是资源的互斥访问,一般需要资源加锁以防死锁。
线程池
- 线程池采用预创建的技术,在应用程序启动之后,将立即创建一定数量的线程(N1),放入空闲队列中。这些线程都是处于阻塞(Suspended)状态,不消耗CPU,但占用较小的内存空间。
- 当任务到来后,缓冲池选择一个空闲线程,把任务传入此线程中运行。当N1个线程都在处理任务后,缓冲池自动创建一定数量的新线程,用于处理更多的任务。
- 在任务执行完毕后线程也不退出,而是继续保持在池中等待下一次的任务。
- 当系统比较空闲时,大部分线程都一直处于暂停状态,线程池自动销毁一部分线程,回收系统资源。
- 线程池的主要组成部分有二:
(1)任务队列(Task Queue)
(2)线程池(Thread Pool) - 线程池通常适合下面的几个场合:
(1)单位时间内处理任务频繁而且任务处理时间短
(2)对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。
一个分享,这里,代码非常的简洁,只有一个头文件ThreadPool.h,这里贴出来作为备份。
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
class ThreadPool {
public:
ThreadPool(size_t);
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::thread > workers;
// the task queue
std::queue< std::function<void()> > tasks;
// synchronization
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
: stop(false)
{
for(size_t i = 0;i<threads;++i)
workers.emplace_back(
[this]
{
for(;;)
{
std::function<void()> task;
{
// 锁定互斥锁以确保没有其他人正在访问该资源
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock,
[this]{ return this->stop || !this->tasks.empty(); });
if(this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
}
);
}
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
// 创建一个执行
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread &worker: workers)
worker.join();
}
#endif
#include <iostream>
#include "ThreadPool.h"
void func()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout<<"worker thread ID:"<<std::this_thread::get_id()<<std::endl;
}
int main()
{
ThreadPool pool(4);
while(1)
{
pool.enqueue(fun);
}
}
锁与线程
参考这个较为详细,这里
- 死锁:当两个以上的运算单元,双方都在等待对方停止运行,以获取系统资源,但是没有一方提前退出时,就称为死锁。
- 死锁的基本条件:
禁止抢占(no preemption):系统资源不能被强制从一个进程中退出;
持有和等待(hold and wait):一个进程可以在等待时持有系统资源;
互斥(mutual exclusion):资源只能同时分配给一个进程或者线程,无法多个进程或者线程共享;
循环等待(circular waiting):一系列进程互相持有其他进程所需要的资源。 - 死锁的四个条件缺一不可,一般如果需要解决死锁,破坏其中一个即可。
版权声明:本文为heroybc原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。