🐶博主主页:@ᰔᩚ. 一怀明月ꦿ
❤️🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux
🔥座右铭:“不要等到什么都没有了,才下定决心去做”
🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
目录
信号量
sem_init
sem_destroy
sem_wait
sem_post
基于环形队列的CP问题
环形队列的生产者消费者模型
线程池
localtime时间戳转化
mkdir系统调用
bind绑定成员函数
懒汉饿汉单例模式
懒汉模式:
饿汉模式:
懒汉和饿汉相同点:
线程池源码实现
信号量
1)信号量本质就是一把计数器
2)申请信号量本质就是预订资源
3)PV操作是原子的
PV操作是信号量的基本操作,用于对信号量的值进行增加(V操作)和减少(P操作)
快速认识信号量的接口
头文件:#include<semaphore.h>
sem_init
在Linux中,sem_init 函数用于初始化一个未命名的信号量。
其函数声明如下:
int sem_init(sem_t *sem, int pshared, unsigned int value); * sem:指向要初始化的信号量的指针。 * pshared:指定信号量是在进程间共享(非0)还是在当前进程内共享(0)。 * value:指定信号量的初始值。
sem_destroy
sem_destroy 函数用于销毁一个未命名的信号量。
其函数声明如下:
int sem_destroy(sem_t *sem);
sem_wait
在Linux中,sem_wait 函数用于执行一个P操作,即等待(wait)信号量的值减少。
其函数声明如下:
int sem_wait(sem_t *sem); * sem:指向要操作的信号量的指针。 当调用 sem_wait 函数时,它会尝试减少信号量的值。如果信号量的值大于0, 则将其减少;如果信号量的值已经为0,则该操作将被阻塞,直到信号量的值不为0。 一旦成功执行了P操作,即信号量的值被减少,进程或线程可以继续执行接下来的代码段。
sem_post
在Linux中,sem_post 函数用于执行一个V操作,即增加(post)信号量的值。
其函数声明如下:
int sem_post(sem_t *sem); * sem:指向要操作的信号量的指针。 当调用 sem_post 函数时,它会增加信号量的值。这通常用于释放资源或信号其他进程或线程可以继续执行。 如果有其他进程或线程正在等待该信号量(通过 sem_wait 阻塞),则调用 sem_post 后会使其中一个等 待的进程或线程继续执行。
基于环形队列的CP问题
1.生产者不能把消费者套圈
2.消费者不能超过生产者
生产者和消费者只有两种场景指向同一个位置
1)为空:只能让生产者跑(互斥)
2)未满:只能让消费者跑(互斥)
其他情况,生产者和消费者不会指向同一个位置(同步)
伪代码
资源分为空间和数据
需要两个信号量 Sem_space Sem_data生产者: {P(sem_space)//生产行为V(sem_data) }生产者: {P(sem_data)//生产行为V(sem_space) }
多生产者和多消费者
为了保证临界资源的安全性,我们需要通过加锁实现生产者之间的互斥
现在就有一个问题?
我们线程是先申请资源还是,先申请信号量呢?
答案是,先申请信号量,因为我们可以让多个生产者先申请信号量,然后阻塞在加锁哪里,这样就可以节省申请信号量的时间
环形队列的生产者消费者模型
线程池
localtime时间戳转化
在 Linux 中,localtime 函数是用于将时间戳(自 1970 年 1 月 1 日以来的秒数)转换为本地时间的函数。
它的函数原型通常在 time.h 头文件中声明
struct tm *localtime(const time_t *timep); 这个函数接受一个 time_t 类型的指针作为参数,返回一个指向 tm 结构体的指针, 该结构体包含了年、月、日、时、分、秒等本地时间信息。
mkdir系统调用
在Linux中,mkdir是一个系统调用,用于创建新的目录。系统调用是操作系统提供给应用程序的接口,允许应用程序直接请求操作系统执行某些特定的操作。
mkdir系统调用的原型如下:
#include <sys/stat.h> #include <sys/types.h>int mkdir(const char *pathname, mode_t mode); pathname:要创建的目录的路径名。 mode:新目录的权限模式。可以使用chmod符号或八进制表示(例如0755),用于指定新目录的权限。 通常情况下,新目录默认权限为0777,但会受到umask设置的影响。
以下是一个简单的C语言示例,展示了如何使用mkdir系统调用来创建新的目录:
#include <sys/stat.h> #include <sys/types.h> #include <stdio.h> int main() {const char* dir = "/path/to/new/dir";mode_t mode = 0777;int result = mkdir(dir, mode);if (result == 0) {printf("Directory created successfully.\n");} else {perror("Error creating directory");}return 0; } 请注意,为了使用mkdir系统调用,你需要包含sys/types.h和sys/stat.h头文件,并链接libc库( 使用gcc时会自动链接)。
bind绑定成员函数
bind () 函数 std::bind()函数作为函数的适配器,它可以扩大函数是使用场合,使得函数更加灵活的被使用。 template<class F, class… Args> bind(F&&f, Args&&… args); 参数: f 可以是function object,函数指针,函数引用,成员函数指针,或者数据成员的指针,lambda表达式。 返回值:function object事例 #include <iostream> #include <functional>using namespace std; using namespace std::placeholders;class test_callback { public:test_callback(void):a(10),b(100){ }typedef function<void(int,int)> callback;void use_value(callback func) {cout << "value a is " << a << endl;cout << "value b is " << b << endl;func(a, b);} private:int a;int b; };class client { public:client(){ this->value = 2; }static void print_sum(int a, int b, int c) {cout << a + b +c << endl;}void print_multiply(int a, int b, int c, int d) {cout << a * b * c * d << endl;cout << "client value is " << this->value << endl; } private:int value; };int main(int argc, char *argv[]) {test_callback test1;client client1;test1.use_value(bind(client::print_sum, _2, _1, 0));test1.use_value(bind(&client::print_multiply, &client1, _1, _2, 2, 3));return 0; } 重点是在使用non static function时要加&获取nonstatic 成员函数地址 结果: value a is 10 value b is 100 110 value a is 10 value b is 100 6000 client value is 2
懒汉饿汉单例模式
C++中的单例模式、懒汉模式和饿汉模式。单例模式在面试和实际开发中都非常重要,其构造函数和instance成员变量分别采用private和static修饰,通过get_instance函数获取单例对象。懒汉模式存在线程安全问题,可通过添加锁解决。饿汉模式则是懒汉模式的一种优化,通过提前加载instance避免线程安全问题。
饿汉模式:程序运行即创建对象并实例化,静态实现所以线程是安全的懒汉模式:创建对象不实例化,需要的时候才实例化,线程不安全需要(加锁)
懒汉模式:
1)构造函数设为私有
2)instance//单例为静态成员变量,类内声明,类外初始化static 类名* instance
类名* 类名::instance=nullptr;//类外定义
3)创建对外接口,通过这个获取单例Static 类名* getinstance() {//这里为什么要用俩个if呢?因为我们在内层if进行加锁了,加锁为了保证只有一个线程去创建单例。其实可以把锁加在外层if,但是这样会导致每个线程都会去申请锁,这样导致资源浪费,所以我们把锁加在内层的if,只要有一个线程创建了单例,那其他线程就不会进入到内层ifif(instance==nullptr){//这里可以加一把锁if(instance==nullptr){instance=new 类名();}//解锁}return instance; }
饿汉模式:1)构造函数设为私有
2)instance//单例为静态成员变量,类内声明,类外初始化static 类名* instance
类名* 类名::instance=new 类名();//类外定义
3)创建对外接口,通过这个获取单例Static 类名* getinstance() {return instance; }
懒汉和饿汉相同点:
1)都需要将构造函数设置为私有的
2)都需要静态的类类型指针变量
懒汉和饿汉不同点:
懒汉你要使用的时候才创建单例(多线程中,是线程不安全的,可以通过加锁实现安全)
饿汉是使用之前就创建单例(多线程中,是线程安全的)使用懒汉单例模式的原因
懒汉单例模式的主要作用是在第一次使用类时才创建类的实例,从而节省资源。在某些情况下,类的实例在程序运行期间只需要创建一次,如果提前创建,可能会浪费资源。懒汉单例模式就是在这种情况下使用的。例如,线程池类在程序运行期间只需要创建一次,如果提前创建,可能会导致线程在等待任务时提前创建线程,从而浪费资源。使用懒汉单例模式,可以在第一次使用线程池时才创建线程池实例,从而避免这种情况。总结来说,懒汉单例模式的主要作用是在第一次使用类时才创建类的实例,从而节省资源。
线程池源码实现
🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸