线程与进程的对比
线程的概念是共享CPU的需要,进程概念是共享内存的需要。
一个进程里代码段,数据段、堆是共享的,但是进程中的每个线程中的栈、寄存器内容独立;
进程都是独立的,通常的IPC,管道,共享内存都可以通讯。
处于一个线程的代码觉得它拥有整个CPU,处于一个进程的代码觉得它拥有全部内存条
多线程是通过时间片轮询来执行的
当某个线程持有这把锁的时候(加锁),该线程就是独占所有的资源,这里指执行的权限,其他要抢夺的资源不得不改变。
当线程拥有某把锁开始上锁以后,那么在锁住的区域,资源就被独占了,其他线程不能抢夺这里的资源。
C++11 多线程编程之条件变量std:: condition_variable、wait()、notify_one()、notify_all()
参考https://blog.csdn.net/hltt3838/article/details/119844807
- 为何引入条件变量 ?
解决while不断循环收发消息,让它只有消息到来时才进行处理。大大减少CPU的使用率和提高程序效率;
在多线程编程中,当多个线程之间需要进行某些同步机制时,如某个线程的执行需要另一个线程完成后才能进行,可以使用条件变量;
c++11提供的 condition_variable 类是一个同步原语,它能够阻塞一个或者多个线程,直到另一线程修改共享变量并通知 condition_variable。也可以把它理解为信号通知机制,一个线程负责发送信号,其他线程等待该信号的触发。
- 条件变量 std:: condition_variable
std:: condition_variable是条件变量,实际上是个类,是一个与条件相关的类,说白了就是等待一个条件的达成。这个类是需要和互斥量来配合工作的,使用时定义一个该类对象即可。
- notify_one()与notify_all()
notify_one() 与 notify_all() 常用来唤醒阻塞的线程;
notify_one():因为只唤醒等待队列中的第一个线程;不存在锁争用,所以能够立即获得锁;其余的线程不会被唤醒,需要等待再次调用notify_one()或者notify_all();
notify_all():会唤醒所有等待队列中阻塞的线程,存在锁争用,只有一个线程能够获得锁。那其余未获取锁的线程接着会怎么样?会阻塞?还是继续尝试获得锁?答案是会继续尝试获得锁(类似于轮询),而不会再次阻塞。当持有锁的线程释放锁时,这些线程中的一个会获得锁。而其余的会接着尝试获得锁。
例子:
1.生产者和消费者模型
2.互斥锁和条件变量实现线程池 代码不是很能看的懂
#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <vector>
#include <queue>
#include <thread>
#include <iostream>
#include <condition_variable>
using namespace std;
const int MAX_THREADS = 1000; //最大线程数目
template <typename T>
class threadPool
{
public:
threadPool(int number = 1);
~threadPool();
bool append(T *task);
//工作线程需要运行的函数,不断的从任务队列中取出并执行
static void *worker(void *arg);
void run();
private:
//工作线程
vector<thread> workThread;
//任务队列
queue<T *> taskQueue;
mutex mt;
condition_variable condition;
bool stop;
};
template <typename T>
threadPool<T>::threadPool(int number) : stop(false)//注意这里类模板实现类的构造函数的写法
{
if (number <= 0 || number > MAX_THREADS)
throw exception();
for (int i = 0; i < number; i++)
{
cout << "create thread:" << i << endl;
workThread.emplace_back(worker, this);
}
}
template <typename T>
inline threadPool<T>::~threadPool()
{
{
unique_lock<mutex> unique(mt);
stop = true;
}
condition.notify_all();
for (auto &wt : workThread)
wt.join();
}
template <typename T>
bool threadPool<T>::append(T *task)
{
//往任务队列添加任务的时候,要加锁,因为这是线程池,肯定有很多线程
unique_lock<mutex> unique(mt);
taskQueue.push(task);
unique.unlock();
//任务添加完之后,通知阻塞线程过来消费任务,有点像生产消费者模型
condition.notify_one();
return true;
}
template <typename T>
void *threadPool<T>::worker(void *arg)
{
threadPool *pool = (threadPool *)arg;
pool->run();
return pool;
}
template <typename T>
void threadPool<T>::run()//消费者阶段
{
while (!stop)
{
unique_lock<mutex> unique(this->mt);
//如果任务队列为空,就停下来等待唤醒,等待另一个线程发来的唤醒请求
while (this->taskQueue.empty())//这个while其实只判断了一次,如果确实为空,那么就停下来等待唤醒
this->condition.wait(unique);
T *task = this->taskQueue.front();
this->taskQueue.pop();
if (task)
task->process();
}
}
#endif
3.信号量的实现
死锁
两个函数初始化成线程实现的时候,当时间片轮询到某一线程的时候,并不是把该线程的所有函数语句都执行完才到另一个线程,可能几轮的for循环,在执行完第一轮就到另一个线程中执行了。所以如果两个线程中用到了相同的锁,很有可能导致死锁的问题。
https://blog.csdn.net/hltt3838/article/details/120365192