线程与进程的对比、互斥锁和条件变量的使用-多线程编程

线程与进程的对比

线程的概念是共享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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值