【Linux】写个日志和再谈线程池

发布于:2024-03-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:信号量和线程池

在这里插入图片描述


👉🏻日志代码

Log.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;
enum
{
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal
};

enum
{
    Screen = 10,//向显示器
    OneFile,
    ClassFile
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case Debug:
        return "Debug";
    case Info:
        return "Info";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "Unknown";
    }
}

const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";

class Log
{
public:
    Log() : style(defaultstyle), filename(default_filename)
    {
        mkdir(logdir.c_str(), 0775);
    }
    void Enable(int sty) //
    {
        style = sty;
    }
    std::string TimeStampExLocalTime()
    {
        time_t currtime = time(nullptr);
        struct tm* curr = localtime(&currtime);
        char time_buffer[128];
        snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
            curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
            curr->tm_hour, curr->tm_min, curr->tm_sec);
        return time_buffer;
    }
    void WriteLogToOneFile(const std::string& logname, const std::string& message)
    {
        umask(0);
        int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        if (fd < 0) return;
        write(fd, message.c_str(), message.size());
        close(fd);
        // std::ofstream out(logname);
        // if (!out.is_open())
        //     return;
        // out.write(message.c_str(), message.size());
        // out.close();
    }
    void WriteLogToClassFile(const std::string& levelstr, const std::string& message)
    {
        std::string logname = logdir;
        logname += "/";
        logname += filename;
        logname += levelstr;
        WriteLogToOneFile(logname, message);
    }

    void WriteLog(const std::string& levelstr, const std::string& message)
    {
        switch (style)
        {
        case Screen:
            std::cout << message;
            break;
        case OneFile:
            WriteLogToClassFile("all", message);
            break;
        case ClassFile:
            WriteLogToClassFile(levelstr, message);
            break;
        default:
            break;
        }
    }
    void LogMessage(int level, const char* format, ...) // 类C的一个日志接口
    {
        char leftbuffer[1024];
        std::string levelstr = LevelToString(level);
        std::string currtime = TimeStampExLocalTime();
        std::string idstr = std::to_string(getpid());

        char rightbuffer[1024];
        va_list args; // char *, void *
        va_start(args, format);
        // args 指向了可变参数部分
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
        va_end(args); // args = nullptr;
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
            levelstr.c_str(), currtime.c_str(), idstr.c_str());

        std::string loginfo = leftbuffer;
        loginfo += rightbuffer;
        WriteLog(levelstr, loginfo);
    }
    // void operator()(int level, const char *format, ...)
    // {
    //     LogMessage(int level, const char *format, ...)
    // }
    ~Log() {}

private:
    int style;
    std::string filename;
};

Log lg;

class Conf
{
public:
    Conf()
    {
        lg.Enable(Screen);
    }
    ~Conf()
    {}
};

Conf conf;

Main.cc

#include <iostream>
#include "Log.hpp"

int main()
{
    Log lg;
    lg.Enable(OneFile);
    lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);
    lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);
    return 0;
}

上述我们是将内容写进一个文件夹里lg.Enable(OneFile);
我们看结果:
在这里插入图片描述

👉🏻线程池代码

LockGuard.hpp(自定义互斥锁,进行加锁和解锁)

#pragma once

#include <pthread.h>

// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock):_lock(lock)
    {}
    void Lock()
    {
        pthread_mutex_lock(_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(_lock);
    }
    ~Mutex()
    {}

private:
    pthread_mutex_t *_lock;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock): _mutex(lock)
    {
        _mutex.Lock();
    }
    ~LockGuard()
    {
        _mutex.Unlock();
    }
private:
    Mutex _mutex;
};

Thread.hpp

#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>

// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;

template<class T>
class Thread
{
public:
    Thread(const std::string &threadname, func_t<T> func, T &data)
    :_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data)
    {}

    static void *ThreadRoutine(void *args) // 类内方法,
    {
        // (void)args; // 仅仅是为了防止编译器有告警
        Thread *ts = static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

    bool Start()
    {
        int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);
        if(n == 0) 
        {
            _isrunning = true;
            return true;
        }
        else return false;
    }
    bool Join()
    {
        if(!_isrunning) return true;
        int n = pthread_join(_tid, nullptr);
        if(n == 0)
        {
            _isrunning = false;
            return true;
        }
        return false;
    }
    std::string ThreadName()
    {
        return _threadname;
    }
    bool IsRunning()
    {
        return _isrunning;
    }
    ~Thread()
    {}
private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

Task.hpp(安排线程任务)

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>

const int defaultvalue = 0;

enum
{
    ok = 0,
    div_zero,
    mod_zero,
    unknow
};

const std::string opers = "+-*/%";

class Task
{
public:
    Task()
    {
    }
    Task(int x, int y, char op)
        : data_x(x), data_y(y), oper(op), result(defaultvalue), code(ok)
    {
    }
    void Run()
    {
        switch (oper)
        {
        case '+':
            result = data_x + data_y;
            break;
        case '-':
            result = data_x - data_y;
            break;
        case '*':
            result = data_x * data_y;
            break;
        case '/':
        {
            if (data_y == 0)
                code = div_zero;
            else
                result = data_x / data_y;
        }
        break;
        case '%':
        {
            if (data_y == 0)
                code = mod_zero;
            else
                result = data_x % data_y;
        }

        break;
        default:
            code = unknow;
            break;
        }
    }
    void operator()()
    {
        Run();
    }
    std::string PrintTask()
    {
        std::string s;
        s = std::to_string(data_x);
        s += oper;
        s += std::to_string(data_y);
        s += "=?";

        return s;
    }
    std::string PrintResult()
    {
        std::string s;
        s = std::to_string(data_x);
        s += oper;
        s += std::to_string(data_y);
        s += "=";
        s += std::to_string(result);
        s += " [";
        s += std::to_string(code);
        s += "]";

        return s;
    }
    ~Task()
    {
    }

private:
    int data_x;
    int data_y;
    char oper; // + - * / %

    int result;
    int code; // 结果码,0: 结果可信 !0: 结果不可信,1,2,3,4
};

ThreadPool.hpp

#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"

static const int defaultnum = 5;

class ThreadData
{
public:
    ThreadData(const std::string &name) : threadname(name)
    {
    }
    ~ThreadData()
    {
    }

public:
    std::string threadname;
};

template <class T>
class ThreadPool
{
private:
    ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num)
    {
        pthread_mutex_init(&_mutex, nullptr);//互斥锁初始化
        pthread_cond_init(&_cond, nullptr);//条件变量初始化
        // 构建指定个数的线程
        for (int i = 0; i < _thread_num; i++)
        {
            // 待优化
            std::string threadname = "thread-";
            threadname += std::to_string(i + 1);

            ThreadData td(threadname);

            // Thread<ThreadData> t(threadname,
            //                      std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);
            // _threads.push_back(t);
            _threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun, this,std::placeholders::_1),td);//emplace_back函数直接在容器的末尾就地构造一个新的元素(直接构造),而不是先创建一个临时对象
            
            lg.LogMessage(Info, "%s is created...\n", threadname.c_str());//打印日志
        }
    }
    ThreadPool(const ThreadPool<T> &tp) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;

public:
    // 有线程安全问题的(单例模式-懒汉模式):在懒汉模式下,单例实例在第一次使用时才被创建
    //线程不安全是因为会有多个线程进行创建实例
    static ThreadPool<T> *GetInstance()
    {
        if (instance == nullptr)
        {
            LockGuard lockguard(&sig_lock);//为了规避线程不安全情况,要进行加锁操作
            if (instance == nullptr)
            {
                lg.LogMessage(Info, "创建单例成功...\n");
                instance = new ThreadPool<T>();
            }
        }

        return instance;
    }
    bool Start()
    {
        // 启动
        for (auto &thread : _threads)
        {
            thread.Start();
            lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
        }

        return true;
    }
    void ThreadWait(const ThreadData &td)//线程等待
    {
        lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
        pthread_cond_wait(&_cond, &_mutex);
    }
    void ThreadWakeup()//唤醒线程
    {
        pthread_cond_signal(&_cond);
    }
    void checkSelf()
    {
        // 1. _task_num > _task_num_high_water && _thread_num < _thread_num_high_water
        // 创建更多的线程,并且更新_thread_num

        // 2. _task_num == _task_num_low_water && _thread_num >= _thread_num_high_water
        // 把自己退出了,并且更新_thread_num
    }
    void ThreadRun(ThreadData &td)//线程执行
    {
        while (true)
        {
            // checkSelf()
            checkSelf();
            // 取任务
            T t;
            {
                LockGuard lockguard(&_mutex);
                while (_q.empty())
                {
                    ThreadWait(td);//执行队列如果为空,则让当前线程进行等待
                    lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());//若执行到这一步,说明线程以及被唤醒
                }
                t = _q.front();
                _q.pop();
            }
            // 处理任务
            t();
            lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
                          td.threadname, t.PrintTask().c_str(), t.PrintResult().c_str());
        }
    }
    void Push(T &in)
    {
        lg.LogMessage(Debug, "other thread push a task, task is : %s\n", in.PrintTask().c_str());
        LockGuard lockguard(&_mutex);
        _q.push(in);
        ThreadWakeup();
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    // for debug
    void Wait()
    {
        for (auto &thread : _threads)
        {
            thread.Join();
        }
    }

private:
    std::queue<T> _q;
    std::vector<Thread<ThreadData>> _threads;
    int _thread_num;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *instance;
    static pthread_mutex_t sig_lock;
    // 扩展1:
    // int _thread_num;
    // int _task_num;

    // int _thread_num_low_water;  // 3
    // int _thread_num_high_water; // 10
    // int _task_num_low_water;    // 0
    // int _task_num_high_water;   // 30

    // 扩展2: 多进程+多线程

    // int number{1};
};

template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

👉🏻读写锁和自旋锁

当多个线程尝试同时访问共享资源时,为了保证数据的一致性,需要使用锁来实现同步。读加锁和自旋锁都是常见的锁机制,用于控制对共享资源的访问。

  1. 读加锁(Read Lock):

    • 读加锁允许多个线程同时对共享资源进行读取操作,但不允许写操作。
    • 当一个线程获取了读锁后,其他线程可以继续获取读锁,但不能获取写锁,直到所有的读锁都被释放。
    • 读锁之间不会互斥,可以同时存在多个读锁
    • 读锁适用于读操作频繁,写操作较少的场景,可以提高并发读取效率。
  2. 自旋锁(Spin Lock):

    • 自旋锁是一种忙等待的锁机制,当一个线程尝试获取锁时,如果锁已经被其他线程占用,该线程会循环检测锁是否被释放,而不是进入睡眠状态
    • 自旋锁适用于对共享资源的访问时间非常短的情况,避免了线程频繁切换和上下文切换的开销
    • 自旋锁在多核处理器上效果更好,因为在等待锁的过程中可以利用处理器时间进行自旋操作,提高效率。

总的来说,读加锁适合读操作频繁的场景,而自旋锁适合对共享资源访问时间短、线程并发量不高的场景。选择合适的锁机制可以提高程序的并发性能和效率。

c++读写锁实现伪代码

读写锁(Read-Write Lock)是一种多线程同步机制,允许多个线程同时对共享资源进行读取操作,但在进行写操作时需要互斥排他。它的设计目的是在读操作频繁、写操作较少的场景下提高并发性能。

读写锁通常包括两种状态:读模式和写模式。

  • 读模式:多个线程可以同时获取读锁,以便并发地读取共享资源。
  • 写模式:当一个线程获取写锁时,其他线程无法同时获取读锁或写锁,确保写操作的互斥性。

与互斥锁不同,读写锁允许多个线程同时持有读锁,这样可以提高并发性能。只有当有线程持有写锁时,其他线程才不能获取读锁或写锁。

读写锁的使用场景通常是在数据的读操作远远多于写操作的情况下,通过允许并发读取来提高性能。然而,如果写操作频繁,读写锁可能会失去优势,因为写操作会阻塞所有的读操作。

在C++中,可以使用 std::shared_mutex 来实现读写锁的功能,通过 lock_shared() 获取读锁,通过 lock() 获取写锁,实现对共享资源的安全访问。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int sharedData = 0;

void writer() {
    mtx.lock(); // 加锁
    sharedData++; // 写操作
    std::cout << "Writer updated data to: " << sharedData << std::endl;
    mtx.unlock(); // 解锁
}

int main() {
    std::thread writerThread1(writer);
    std::thread writerThread2(writer);

    writerThread1.join();
    writerThread2.join();

    return 0;
}

c++自旋锁实现伪代码

C++11 提供的原子操作来实现自旋锁

#include <atomic>

class SpinLock {
public:
    void lock() {
        while (locked.test_and_set(std::memory_order_acquire)) {
            // 自旋等待锁释放
        }
    }

    void unlock() {
        locked.clear(std::memory_order_release);
    }

private:
    std::atomic_flag locked = ATOMIC_FLAG_INIT;
};


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看